我的<main>
、<div>
和<my-directive>
之间有一个父子控制器关系,如下所示:
<main ng-controller="MainController">
<nav>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
</nav>
<div ng-controller="AboutController">
</div>
<my-directive></my-directive>
</main>
在主控制器中,我使用以下命令执行$broadcast:
$scope.$broadcast('shoppingCartReady', msg);
在AboutController中,我可以通过以下方式成功接收此$broadcast:
angular.module('aboutModule', [])
.controller('AboutController', require('./about/aboutController'));
var AboutController = function ($scope, $rootScope) {
$scope.$on('shoppingCartReady', function(event, msg){
console.log(msg);
});
};
module.exports = [ '$scope', '$rootScope', AboutController];
但是,当尝试在链接函数<my-directive>
接收$broadcast时,似乎永远不会收到:
angular.module('shopModule', [])
.directive('shopModuleDirective', require('./shopModuleDirective'))
.directive('myDirective', require('./myDirective'));
var myDirective = function ($rootScope) {
function link($scope, element, attrs, baseCtrl) {
$scope.$on('shoppingCartReady', function (event, msg) {
console.log(msg);
});
}
return {
restrict: 'E',
require: '^shopModuleDirective',
link: link
};
};
return ['$rootScope', myDirective];
更新
我什至在我的指令中创建了一个控制器:
angular.module('shopModule', [])
.directive('shopModuleDirective', require('./shopModuleDirective'))
.directive('myDirective', require('./myDirective'));
var myDirective = function ($rootScope) {
var controller = ["$scope", "$element", function ($scope, element) {
console.log('waiting for .on....');
$scope.$on('shoppingCartReady', function (event, cache) {
console.log('shoppingCartReady .on rcvd!');
});
}]
function link($scope, element, attrs, baseCtrl) {
$scope.$on('shoppingCartReady', function (event, msg) {
console.log(msg);
});
}
return {
restrict: 'E',
require: '^shopModuleDirective',
link: link
};
};
return ['$rootScope', myDirective];
我可以看到日志
console.log('waiting for .on....');
但是 .on 从未收到过。
更新:
我认为可能的原因是由于范围没有"准备好"。
在我从主控制器获得的原始broadcast
中,如果我在setTime
后执行另一个,则会收到所有.on
:
主控制器:
$scope.$broadcast('shoppingCartReady', msg);
setTimeout(function(){
$scope.$broadcast('shoppingCartReady', msg);
}, 8000);
来自文档:
$broadcast(名称,参数);
将事件名称向下调度到所有子作用域(及其 子项)通知已注册的$rootScope.作用域侦听器。
就像在您的指令中一样,您没有创建新范围,我想您只是与您的MainController
处于同一范围级别。在这种情况下,您不在 MainController
的子范围中,因此无法在那里触发事件。我会尝试以下方法之一:
-
始终通过
$rootScope.$broadcast
广播您的活动。所有其他范围都是$rootScope
的子级,因此无论您在哪里注册$scope.$on
事件,您都会收到它。您可能还对为什么我们在 AngularJS 中使用 $rootScope.$broadcast 感兴趣? -
给你的指令一个隔离的
$scope
。这将充当MainController
的子范围,并且也应该起作用。隔离指令的$scope。 -
除了选项
1.
您还可以将$rootScope.$emit
与$rootScope.$on
一起使用。有关更多信息,请阅读 $rootScope.$broadcast vs. $scope.$emit
我总是选择选项 1.
因为有时您不希望您的指令具有孤立的范围。
存在竞争条件。
myDirective
控制器和后链接功能在MainController
后执行,侦听器在触发事件后shoppingCartReady
设置。
好的、可测试的指令设计的经验法则是将所有与范围相关的逻辑保留在控制器中,链接函数和$onInit
钩子完全用于应该在那里发生的事情,而不是其他任何地方,比如对编译的 DOM 内容的操作。
不太明显但独特的link
用例是理想的执行顺序(链接后函数以相反的顺序执行,从子项到父级)。如果MainController
成为指令,并且scope.$broadcast('shoppingCartReady', msg)
在函数而不是控制器link
执行,则保证了正确的执行顺序。
将控制器代码包装为零$timeout
也会推迟代码
$timeout(function(){
$scope.$broadcast('shoppingCartReady', msg);
});
在链接子指令中的函数后执行,但也会指定一些代码气味。这就是为什么作用域事件是不太理想的指令通信方式并且容易成为反模式的原因之一。
处理此问题的替代方法取决于shoppingCartReady
事件的内容,但在当前情况下它是多余的:在执行子控制器的那一刻,它已经发生了,他们可以安全地假设"购物车已准备就绪"。
本主题的推荐阅读:AngularJS 中的指令控制器和链路时序。