Angular按照發布/訂閱模式設計了其事件系統,使用時須要「發佈」事件,並在適當的位置「訂閱」或「退訂」事件,就像郵箱裏面大量的訂閱郵件同樣,當咱們不須要時就能夠將其退訂了。具體到開發中,對應着$scope和$rootScope的$emit
、$broadcast
和$on
方法。本文介紹Angular的事件機制,包括$scope和$rootScope處理事件上的異同,$broadcast、$emit和$on的使用方式及他們區別等內容。javascript
要理解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
是自下而上的射箭,只有在箭矢的軌跡上才能對其作出反應。設計
具體到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
是全部$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.$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) {...});