![](/img/trans.png)
[英]angularjs: $watch not triggering on service variable updated if the service is called by directive
[英]AngularJS : service $broadcast and $watch not triggering on receiving controller
AngularJS noob在我的Angular Enlightenment路上:)
情况如下:
我在我的模块'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服务上调用相同的方法。
问题
当有手动交互时(即当我点击下一个()按钮时)这很好 - 流程如下:
但是,当在“背景”中从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音频播放器,但我认为这不应该归咎于此......)
当您有一个click事件时,$ scope会更新,而不需要使用$ apply
$scope.$apply(function () {
$scope.current = track;
});
因为查看摘要内部结构是不安全的,最简单的方法是使用$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
优势$scope.$apply
:您不必知道自己是否处于摘要周期。 $timeout
优点:你并不是真的想要超时,而是在没有额外0
参数的情况下获得更简单的语法。 // apply changes
$scope.current = track;
try {
if (!$scope.$$phase) {
$scope.$apply($scope.current);
}
} catch (err) {
console.log(err);
}
尝试了一切,它使用$rootScope.$applyAsync(function() {});
为我工作$rootScope.$applyAsync(function() {});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.