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


Angular按照發布/訂閱模式設計了其事件系統,使用時需要“發布”事件,並在適當的位置“訂閱”或“退訂”事件,就像郵箱里面大量的訂閱郵件一樣,當我們不需要時就可以將其退訂了。具體到開發中,對應着$scope和$rootScope的$emit$broadcast$on方法。本文介紹Angular的事件機制,包括$scope和$rootScope處理事件上的異同,$broadcast、$emit和$on的使用方式及他們區別等內容。

$scope與$scope之間的關系,$scope與$rootScope之間的關系

要理解Angular的事件機制,首先需要了解$scope$scope之間的關系以及$scope$rootScope之間的關系。$rootScope是唯一真神,是萬域起源,是所有$scope的最終祖先。而$scope$scope之間可能的關系包括父子關系和兄弟關系。還記得controller之間的關系嗎,Angular為每個controller分配一個獨立的$scope,controller之間的關系也對應着$scope之間的關系:

<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用於發布事件,他們將事件名稱和事件內容發布出去,就像是高考榜單一樣,事件名稱相當於考生的名字,而事件內容相當於考生的成績等信息:

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

 

$on用於訂閱事件,事件名稱是訂閱的唯一標識,每個考生看榜單時都要尋找自己的名字,然后根據自己的成績等信息決定下一步應該報考什么學校:

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

 

Angular的退訂事件有些奇怪,並沒有類似於其他語言的$off方法,所以不要想當然的按照如下方式進行事件的退訂操作:

// 不要這樣做 $scope.$off('EVENT_NAME');

 

事實上,Angular的事件退訂方法隱藏在事件訂閱里面:使用$on訂閱事件時會返回一個函數,而此函數就是用來退訂事件的方法,就像是考生看到了自己的成績后稟告父母大人,“商量着”選取學校填報志願,而此志願單就是結束整個高考榜單的結束:

// 訂閱事件返回用於退訂事件的函數 var deregister = $scope.$on('EVENT_NAME', function(event, args) { // balabala }); // 退訂事件 deregister();

 

$broadcast相當於戰斗機轟炸,$emit相當於射箭

$broadcast$emit都用於發布事件,但從名字就可以看出他們的不同點:$broadcast是自上而下的廣播,所有能聽到的都可以對其進行反應。而$emit是自下而上的射箭,只有在箭矢的軌跡上才能對其做出反應。

具體到Angular上,即從一個$scope上通過$broadcast發布的事件,他的所有后代$scope都可以對此事件做出響應:

// 父$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可以做出響應,並且其中任一祖先都可以將此事件終結掉,不讓其繼續傳播:

// 子$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) {...});


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM