指令中未接收到广播。
我在我的 <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>
在 MainController 中,我使用 $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
都会被接收到:
MainController:
$scope.$broadcast('shoppingCartReady', msg);
setTimeout(function(){
$scope.$broadcast('shoppingCartReady', msg);
}, 8000);
有竞争条件。
myDirective
控制器和后链接函数在MainController
之后执行,并且监听器在shoppingCartReady
事件触发后设置。
一个好的、可测试的指令设计的经验法则是将所有与作用域相关的逻辑放在控制器中,链接函数和$onInit
钩子用于那些应该在那里发生而不是在其他任何地方发生的事情,例如对编译后的DOM内容的操作。
link
不那么明显但独特的用例是所需的执行顺序(后链接函数按相反顺序执行,从子元素到父元素)。如果MainController
成为一个指令,并且scope.$broadcast('shoppingCartReady', msg)
在link
函数中而不是在控制器中执行,这将保证适当的执行顺序。
将控制器代码包装成零$timeout
也将推迟代码
$timeout(function(){
$scope.$broadcast('shoppingCartReady', msg);
});
在子指令的链接函数中执行链接函数,但这也会指定某些代码味道。这是为什么作用域事件是不太可取的指令通信方式,并且容易成为反模式的原因之一。
处理这个的另一种替代方式取决于shoppingCartReady
事件的内容,但在当前情况下它是冗余的:当子控制器执行时,它已经发生了,它们可以安全地假定“购物车已经准备好了”。
这个话题的建议阅读:AngularJS中的指令控制器和链接时间。
从文档中:
由于在指令中您没有创建新的作用域,我猜您只是与
MainController
处于相同的作用域级别。在这种情况下,您不在MainController
的子作用域中,因此无法在那里触发事件。我会尝试以下几种方法之一:始终通过
$rootScope.$broadcast
广播事件。所有其他作用域都是$rootScope
的子级,因此无论您使用$scope.$on
在哪里注册事件,都会接收到该事件。您可能还对为什么在AngularJS中使用$rootScope.$broadcast?感兴趣。为您的指令提供一个隔离的
$scope
。这将作为MainController
的子作用域,并应该正常工作。隔离指令的$scope。除了选项
1.
之外,您还可以使用$rootScope.$emit
和$rootScope.$on
。有关更多信息,请阅读$rootScope.$broadcast vs. $scope.$emit我总是选择选项
1.
,因为有时您不希望指令具有隔离作用域。