【问题标题】:AngularJS : service $broadcast and $watch not triggering on receiving controllerAngularJS:服务 $broadcast 和 $watch 未在接收控制器上触发
【发布时间】:2012-10-17 07:02:16
【问题描述】:

这里是 AngularJS 菜鸟,在我通往 Angular 启蒙的道路上 :)

情况如下:

我已经在我的模块“app”中实现了一个服务“AudioPlayer”并像这样注册:

 app.service('AudioPlayer', function($rootScope) {

     // ...

     this.next = function () {
         // loads the next track in the playlist
         this.loadTrack(playlist[++playIndex]);     
     };

     this.loadTrack = function(track) {
         // ... loads the track and plays it
         // broadcast 'trackLoaded' event when done
         $rootScope.$broadcast('trackLoaded', track); 
     };
 }

这里是“接收器”控制器(主要用于 UI/表示逻辑)

app.controller('PlayerCtrl', function PlayerCtrl($scope, AudioPlayer) {


    // AudioPlayer broadcasts the event when the track is loaded

    $scope.$on('trackLoaded', function(event, track) {
        // assign the loaded track as the 'current' 
        $scope.current = track;
    });

    $scope.next = function() {
        AudioPlayer.next();
    };
}

在我看来,我会像这样显示当前的曲目信息:

<div ng-controller="PlayerCtrl">
    <button ng-click="next()"></button>
    // ...
    <p id="info">{{current.title}} by {{current.author}}</p>
</div>

next() 方法在 PlayerCtrl 中定义,它只是在 AudioPlayer 服务上调用相同的方法。

问题

当有手动交互时(即当我点击 next() 按钮时),这工作正常 - 流程如下:

  1. PlayerCtrl 拦截点击并触发自己的 next() 方法
  2. 依次触发 AudioPlayer.next() 方法
  3. 在播放列表中寻找下一个曲目并调用 loadTrack() 方法
  4. loadTrack() $broadcasts the 'trackLoaded' event(发送轨道本身)
  5. PlayerCtrl 监听广播事件并将轨道分配给当前对象
  6. 视图正确更新,显示 current.title 和 current.author 信息

但是,当在“背景”的 AudioService 中调用 next() 方法时(即,当轨道结束时),从 1 到 5 的所有步骤都会发生,但视图不会得到通知PlayerCtrl 的“当前”对象的变化。

我可以清楚地看到在 PlayerCtrl 中分配的新轨道对象,但好像视图没有收到更改通知。我是菜鸟,我不确定这是否有任何帮助,但我尝试的是在 PlayerCtrl 中添加 $watch 表达式

$scope.$watch('current', function(newVal, oldVal) {
    console.log('Current changed');
})

仅在“手动”交互期间打印出来...

再次,就像我说的,如果我像这样在 $on 监听器中添加一个 console.log(current):

$scope.$on('trackLoaded', function(event, track) {
    $scope.current = track;
    console.log($scope.current);
});

这始终正确打印。

我做错了什么?

(ps 我正在将 AudioJS 用于 HTML5 音频播放器,但我不认为这应该归咎于此......)

【问题讨论】:

    标签: javascript angularjs


    【解决方案1】:

    当你有一个点击事件时,$scope 会更新,没有这个事件你需要使用 $apply

    $scope.$apply(function () {
        $scope.current = track;
    });
    

    【讨论】:

    • 您先生成就了我的一天。但是我发现这样做总是有效的:$scope.current = track; $scope.$apply();
    • 实际上,这会在控制台中给出Error: $digest already in progress...嗯?
    • 你可以检查$scope.$$phase来查看范围是否正在更新,如果是你就不需要$apply
    • @PierloUpitup 最好将它放在$apply 函数中,因为它在try/catch/finally 中运行代码:try/catch 处理错误,finally 使用$digest 运行无论错误如何,您的代码。
    • 使用$timeout。窥视 $scope.$$phase 被认为是接口损坏,并且可能随时中断。 $timeout 保证(使用适当的参数)在 $apply 块中执行函数。
    【解决方案2】:

    由于窥探摘要内部内容并不安全,因此最简单的方法是使用$timeout

    $timeout(function () {
        $scope.current = track;
    }, 0);
    

    回调总是在良好的环境中执行。

    编辑:其实apply阶段应该包装的函数是

     this.loadTrack = function(track) {
         // ... loads the track and plays it
         // broadcast 'trackLoaded' event when done
         $timeout(function() { $rootScope.$broadcast('trackLoaded', track); });
     };
    

    否则会错过直播。

    ~~~~~~

    实际上,替代方案可能更好(至少从语义角度来看),并且它在摘要循环内部或外部同样有效:

    $scope.$evalAsync(function (scope) {
        scope.current = track;
    });
    
    • 相对于$scope.$apply 的优势:您不必知道自己是否处于摘要循环中。
    • 相对于$timeout 的优势:您并不真正想要超时,并且您可以获得更简单的语法而无需额外的0 参数。

    【讨论】:

    • $timeout 执行 $apply 块中的函数。来自角度文档:用法 $timeout(fn[, delay][, invokeApply]);参数 fn – {function()} – 应延迟执行的函数。 delay(optional=0) – {number=} – 延迟以毫秒为单位。 invokeApply(optional=true) – {boolean=} – 如果设置为 false 则跳过模型脏检查,否则将在 $apply 块中调用 fn。
    【解决方案3】:
    // apply changes
    $scope.current = track;
    try {
     if (!$scope.$$phase) {
      $scope.$apply($scope.current);
     }
    } catch (err) {
     console.log(err);
    }
    

    【讨论】:

      【解决方案4】:

      尝试了一切,它对我有用 $rootScope.$applyAsync(function() {});

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-08-27
        • 1970-01-01
        • 2014-12-30
        • 2017-05-03
        • 2018-09-16
        • 1970-01-01
        • 2016-07-14
        • 1970-01-01
        相关资源
        最近更新 更多