Angular $scope和$rootScope事件机制之$emit、$broadcast和$on

Angular按照发布/订阅模式设计了其事件系统,使用时须要“发布”事件,并在适当的位置“订阅”或“退订”事件,就像邮箱里面大量的订阅邮件同样,当咱们不须要时就能够将其退订了。具体到开发中,对应着$scope和$rootScope的$emit$broadcast$on方法。本文介绍Angular的事件机制,包括$scope和$rootScope处理事件上的异同,$broadcast、$emit和$on的使用方式及他们区别等内容。javascript

$scope与$scope之间的关系,$scope与$rootScope之间的关系

要理解Angular的事件机制,首先须要了解$scope$scope之间的关系以及$scope$rootScope之间的关系。$rootScope是惟一真神,是万域起源,是全部$scope的最终祖先。而$scope$scope之间可能的关系包括父子关系和兄弟关系。还记得controller之间的关系吗,Angular为每一个controller分配一个独立的$scope,controller之间的关系也对应着$scope之间的关系:html

<div ng-controller="ParentCtrl as parent"> {{ parent.data }} <div ng-controller="SiblingOneCtrl as sib1"> {{ sib1.data }} </div> <div ng-controller="SiblingTwoCtrl as sib2"> {{ sib2.data }} </div> </div>

 

发布、订阅、退订

$broadcast$emit用于发布事件,他们将事件名称和事件内容发布出去,就像是高考榜单同样,事件名称至关于考生的名字,而事件内容至关于考生的成绩等信息:java

$scope.$broadcast('EVENT_NAME', 'Data to send'); $scope.$emit('EVENT_NAME', 'Data to send');

 

$on用于订阅事件,事件名称是订阅的惟一标识,每一个考生看榜单时都要寻找本身的名字,而后根据本身的成绩等信息决定下一步应该报考什么学校:app

$scope.$on('EVENT_NAME', function(event, args) { // balabala });

 

Angular的退订事件有些奇怪,并无相似于其余语言的$off方法,因此不要想固然的按照以下方式进行事件的退订操做:函数

// 不要这样作 $scope.$off('EVENT_NAME');

 

事实上,Angular的事件退订方法隐藏在事件订阅里面:使用$on订阅事件时会返回一个函数,而此函数就是用来退订事件的方法,就像是考生看到了本身的成绩后禀告父母大人,“商量着”选取学校填报志愿,而此志愿单就是结束整个高考榜单的结束:spa

// 订阅事件返回用于退订事件的函数 var deregister = $scope.$on('EVENT_NAME', function(event, args) { // balabala }); // 退订事件 deregister();

 

$broadcast至关于战斗机轰炸,$emit至关于射箭

$broadcast$emit都用于发布事件,但从名字就能够看出他们的不一样点:$broadcast是自上而下的广播,全部能听到的均可以对其进行反应。而$emit是自下而上的射箭,只有在箭矢的轨迹上才能对其作出反应。设计

具体到Angular上,即从一个$scope上经过$broadcast发布的事件,他的全部后代$scope均可以对此事件作出响应:code

// 父$scope经过$broadcast发布事件 app.controller('ParentCtrl', ['$scope', function($scope) { $scope.$broadcast("parent", 'Data to Send'); }]) //全部子$scope均可以经过$on订阅事件 .controller('SiblingOneCtrl', ['$scope', function($scope) { $scope.$on("parent", function(event, 'Data to Send') { // balabala }); }]) .controller('SiblingTwoCtrl', ['$scope', function($scope) { $scope.$on("parent", function(event, 'Data to Send') { // balabala }); }]);

 

而经过$emit发布的事件,只有他的祖先$scope能够作出响应,而且其中任一祖先均可以将此事件终结掉,不让其继续传播:htm

// 子$scope经过$emit发布事件 app.controller('SiblingOneCtrl', ['$scope', function($scope) { $scope.$emit("sib1", 'Data to Send'); }]) // 父$scope经过$on订阅事件 .controller('ParentCtrl', ['$scope', function($scope) { $scope.$on("sib1", function(event, 'Data to Send') { // balabala }); }]) // 其兄弟$scope对其$emit的事件一无所知,因此不能订阅其事件 .controller('SiblingTwoCtrl', ['$scope', function($scope) { // 不要这样作 $scope.$on("sib1", function(event, 'Data to Send') { // balabala }); }]);

$emit发布事件的响应道路上,其任一祖先若是感受再也不须要此事件了,就能够经过以下方式终结此事件:事件

app.controller('ParentCtrl', ['$scope', function($scope) { $scope.$on("sib1", function(event, 'Data to Send') { // balabala event.stopPropagation(); // 终止事件继续“冒泡” }); }])

 

$rootScope的$broadcast和$emit

上面说过$rootScope是全部$scope的最终祖先,因此经过$rootScope$broadcast发布的事件能够被全部$scope接收到,包括$rootScope

app.controller('SomeCtrl', ['$rootScope', function($rootScope) { $rootScope.$broadcast("rootEvent", 'Data to Send'); // $rootScope也能够经过$on订阅从$rootScope.$broadcast发布的事件 $rootScope.$on("rootEvent", function(event, 'Data to Send') { // balabala }); }]) // 全部$scope都可以经过$on订阅从$rootScope.$broadcast发布的事件 .controller('ParentCtrl', ['$scope', function($scope) { $scope.$on("rootEvent", function(event, 'Data to Send') { // balabala }); }]) .controller('SiblingOneCtrl', ['$scope', function($scope) { $scope.$on("rootEvent", function(event, 'Data to Send') { // balabala }); }])

 

$rootScope$emit就有些怪异了,按照上面的描述,$rootScope是没有祖先的,因此咱们可能会想到其$emit会没有任何做用,但事实并不如此:$rootScope.$emit发布的事件,只能经过$rootScope.$on订阅,而其余$scope对此一无所知:

app.controller('SomeCtrl', ['$rootScope', function($rootScope) { $rootScope.$emit("rootEvent", 'Data to Send'); // 只有$rootScope能够经过$on订阅从$rootScope.$emit发布的事件 $rootScope.$on("rootEvent", function(event, 'Data to Send') { // balabala }); }]) // $scope不可以经过$on订阅从$rootScope.$emit发布的事件 .controller('ParentCtrl', ['$scope', function($scope) { // 不要这样作 $scope.$on("rootEvent", function(event, 'Data to Send') { // balabala }); }]);
  •  

退订$rootScope上的订阅事件

当使用$rootScope.$on订阅事件时,须要手动退订事件,通常在其所处$scope$destory事件中退订:

app.controller('SomeCtrl', ['$rootScope', '$scope', function($rootScope, $scope) { var deregister = $rootScope.$on("rootEvent", function(event, 'Data to Send') { // balabala }); $scope.$on('$destory', function() { deregister(); // 退订事件 }); }])
  •  

那经过$scope.$on订阅的事件呢?通常不须要手动退订,由于Angular会帮咱们退订,可是若是须要本身控制什么时候退订事件,也能够经过上述方式进行退订。

事件命名的建议

在开发中,对于变量的命名、函数的命名、文件的命名都有必定的规范,既要保证可读性,也须要保证无混淆性。在Angular的事件机制中,由于事件可能会跨函数,甚至可能跨文件,因此对于事件名必定要保证惟一性,因此建议事件名都加上特定的前缀,以便区分。以下几个例子:

$scope.$emit('trash:delete', data); $scope.$on('trash:delete', function (event, data) {...}); $scope.$broadcast('trash:clear', data); $scope.$on('trash:clear', function (event, data) {...});
相关文章
相关标签/搜索