AngularJS中控制器之間通信方法


在同個angular應用的控制器之間進行通信可以有很多種不同的方式,本文主要講兩種:

基於scope繼承的方式基於event傳播的方式

 

基於scope繼承的方式

最簡單的讓控制器之間進行通信的方法是通過scope的繼承。假設有兩個控制器Parent、Child,Child 在 Parent 內,那Child 可以稱為子控制器,它將繼承父控制器Parent的scope。這樣,Child就可以訪問到Parent的scope中的所有函數和變量了。
需要注意的是,由於scope的繼承也是基於Js的原型繼承,如果變量是基本類型的,那在Child中的修改(寫),有可能會導致Parent中的數據變臟。

基本類型變量的繼承

 1 function Sandcrawler($scope) {
 2     $scope.location = 'Mos Eisley North';
 3     $scope.move = function(newLocation) {
 4         $scope.location = newLocation;
 5     }
 6 }
 7 function Droid($scope) {
 8     $scope.sell = function(newLocation) {
 9         $scope.location = newLocation;
10     }
11 }
12 // html
13 <div ng-controller="Sandcrawler">
14     <p>Location: </p>
15     <button ng-click="move('Mos Eisley South')">Move</button>
16     <div ng-controller="Droid">
17         <p>Location: </p>
18         <button ng-click="sell('Owen Farm')">Sell</button>
19     </div>
20 </div>

看完上面的代碼我們知道,location 屬性是直接被注冊到 $scope 中的,Droid控制器所擁有的scope從Sandcrawler控制器的scope中繼承了這個屬性並且可以讀取它。看以下兩個假設場景:

  • 如果 Sandcrawler 中改變了 location 屬性,在 Droid 中也會讀取到這個改變;在 view 中的表現則是:點擊了Move 按鈕的話,兩個 p 標簽都會顯示 Mos Eisley South

  • 反過來,如果 Droid 中對 $scope.location 進行改寫,它只改寫自己scope中 location 屬性的值,它不會影響 Sandcrawler 中的這個屬性的值;在 view 中的表現則是:當點擊了 Sell 按鈕之后,兩個控制器scope之間的數據共享就不復存在了,之后無論點多少次 Move 按鈕,都影響不了 Droid 中的 p 標簽的顯示了

經過上面的教訓,有時候我們想要達到的效果可能達不到(如點了 Sell 按鈕之后再點 Move 還想讓它起作用),這樣在ng的開發者中逐漸達成了一個一致的約定,千萬不要把那些可以被子級scope改寫的屬性用基礎類型直接添加在 $scope 對象上,而是應該盡可能地用對象類型去添加。

對象類型變量的繼承

通過上面的結論我們知道,可以用對象類型的變量來作為屬性添加到 $scope 中去,這樣,只要是引用了這個對象的,無論是誰,在哪個控制器里面,對這個對象變量的改寫都會影響都所有引用了這個對象的實例。看下面的代碼:

 1 function Sandcrawler($scope) {
 2     $scope.sandcrawler.location = 'Mos Eisley North';
 3 }
 4 function Droid($scope) {
 5     $scope.summon = function(newLocation) {
 6         $scope.sandcrawler.location = newLocation;
 7     }
 8 }
 9 // html
10 <div ng-controller="Sandcrawler">
11     <p>Sandcrawler Location: </p>
12     <div ng-controller="Droid">
13         <button ng-click="summon('Owen Farm')">
14             Summon Sandcrawler
15         </button>
16     </div>
17 </div>

跑一下上面的代碼就知道,當我們使用“召喚術”的時候,可以改寫 Sandcrawler 控制下的 p 標簽的顯示了。

基於event傳播的方式

基於scope繼承的方式只能處理父子級控制器之間的通信問題,不能處理兄弟/相鄰控制器之間的通信問題。這時候,我們需要使用基於event傳播的方式來進行通信,這里,ng為我們提供了三個方法:$on , $emit ,$broadcast ,需要明確的是:這種方法不僅可以處理兄弟scope間的通信問題,對於解決父子scope間的通信也是毫無壓力。

子-->父:$emit

整個過程是這樣的:

  • 子scope中的控制器通過 $scope.$emit 觸發一個事件向上傳播

  • 這個事件會經過每一層的父scope,至於處不處理是父scope自己的事情了

  • 如果處理,就在想要處理的那個祖先scope中放一個 $scope.$on 監聽着就行了三四三

  •  1 // 父scope上的控制器
     2 function Sandcrawler($scope) {
     3     $scope.location = 'Mos Eisley North';
     4     $scope.$on('summon', function(e, newLocation) {
     5         $scope.location = newLocation;
     6     });
     7 }
     8 // 子scope上的控制器
     9 function Droid($scope) {
    10     $scope.location = 'Owen Farm';
    11     $scope.summon = function() {
    12         $scope.$emit('summon', $scope.location);
    13     }
    14 }
    15 // html
    16 <div ng-controller="Sandcrawler">
    17     <p>Sandcrawler Location: </p>
    18     <div ng-controller="Droid">
    19         <p>Droid Location: </p>
    20         <button ng-click="summon()">Summon Sandcrawler</button>
    21     </div>
    22 </div>

    如果你不想讓你的事件再往更上層傳播,在 $on 中的處理函數調用e.stopPropagation() 即可。

父-->子:$broadcast

從父到子,用另外一個方法就是了,同樣用 $on 監聽着,all done,看下面代碼:

// 父scope上的控制器
function Sandcrawler($scope) {
    $scope.location = 'Mos Eisley North';
    $scope.recall = function() {
        $scope.$broadcast('recall', $scope.location);
    }
}
// 子scope上的控制器
function Droid($scope) {
    $scope.location = 'Owen Farm';
    $scope.$on('recall', function(e, newLocation) {
        $scope.location = newLocation;
    });
}
// html
<div ng-controller="Sandcrawler">
    <p>Sandcrawler Location: </p>
    <button ng-click="recall()">Recall Droids</button>
    <div ng-controller="Droid">
        <p>Droid Location: </p>
    </div>
</div>

同級之間

擁有同個父scope的子級scope之間,也就是兄弟/相鄰scope之間的通信,其實是借助“奶爸”傳遞消息的:

  • 子級scope中有誰想傳消息了,$emit 一個給“奶爸”

  • 然后通過“奶爸” $broadcast 給所有孩子這個相同的信息,當然發出信息的那個可以選擇是否要忽略掉自己發出的信息

 1 // 父scope上的控制器
 2 function Sandcrawler($scope) {
 3     $scope.$on('requestDroidRecall', function(e) {
 4         $scope.$broadcast('executeDroidRecall');
 5     });
 6 }
 7 // 子scope上的控制器
 8 function Droid($scope) {
 9     $scope.location = 'Owen Farm';
10     $scope.recallAllDroids = function() {
11         $scope.$emit('requestDroidRecall');
12     }
13     $scope.$on('executeDroidRecall', function() { 
14         $scope.location = 'Sandcrawler';
15     });
16 }
17 // html
18 <div ng-controller="Sandcrawler">
19     <div ng-controller="Droid">
20         <h2>R2-D2</h2>
21         <p>Droid Location: </p>
22         <button ng-click="recallAddDroids()">Recall All Droids</button>
23     </div>
24     <div ng-controller="Droid">
25         <h2>C-3PO</h2>
26         <p>Droid Location: </p>
27         <button ng-click="recallAddDroids()">Recall All Droids</button>
28     </div>
29 </div>

上面代碼中要注意的是:子scope通過 $emit 發出的事件名不能與父scope用 $broadcast 的事件名一樣,如果有傳參數,那當然參數可以一樣,因為參數就是我們要傳的數據。事件名不能一樣是為了防止進入死循環。

 


免責聲明!

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



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