25

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' 事件(发送轨道本身)
  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 音频播放器,但我认为这不是应该归咎于这里的……)

4

4 回答 4

21

当您有点击事件时, $scope 会更新,如果没有事件,您将需要使用 $apply

$scope.$apply(function () {
    $scope.current = track;
});
于 2012-10-17T13:28:49.907 回答
7

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

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

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

编辑:事实上,应该包装在应用阶段的功能是

 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参数的情况下获得更简单的语法。
于 2013-04-29T09:04:27.723 回答
0
// apply changes
$scope.current = track;
try {
 if (!$scope.$$phase) {
  $scope.$apply($scope.current);
 }
} catch (err) {
 console.log(err);
}
于 2014-02-12T10:43:32.700 回答
0

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

于 2016-03-22T18:27:21.860 回答