上一篇我們延續了 AngularJS 的 MVC 教學,並更深入的解釋 Controller 的一些共通問題
可以看
AngularJS 教學 - Controller 進階(1) 來先對 Controller 有更進一步了解
接下來要說明一個 Controller 繼承的陷阱,還記得上一篇有講到巢狀的 Controller 嗎
其實就是 Scope Inheritance 的意思,以下先用一個範例說明這個陷阱
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" ng-app="demoApp">
<head>
<title>Bootstrap
Examples</title>
<script type="text/javascript" src="js/angular.min.js"></script>
<script>
var
demoApp = angular.module("demoApp", []);
demoApp.controller("parentCtrl1", function($scope){
$scope.count = 1;
$scope.add = function(){
$scope.count++;
};
}).controller("childCtrl1", function($scope){
$scope.minus = function(){
$scope.count--;
};
})
</script>
</head>
<body>
<div ng-controller="parentCtrl1">
<p>Parent
count: {{count}}</p>
<button ng-click="add()">+1</button>
<div ng-controller="childCtrl1">
<p>Child
count: {{count}}</p>
<button ng-click="add()">+1</button><button ng-click="minus()">-1</button>
</div>
</div>
</body>
</html>
先簡單說明一下程式,可以看到我定義了兩個Controller,分別是 parentCtrl1 與 childCtrl1
其中 childCtrl1 繼承 parentCtrl1,而 parentCtrl1 定義一個 add function 會聆聽頁面上
兩個 "+1" 的按鈕事件,而 childCtrl1 則定義一個 minus function 會聆聽頁面上的 "-1" 按鈕事件
其中 add function 雖然 childCtrl1 沒有定義,但因為繼承關係
執行時會去乎叫父類別的 add function,所以也不會有任何問題
$scope.count 也是如此,雖然 childCtrl 內沒定義但是依照繼承關係,也是會找到父類別的
乍看之下沒什麼問題,但在畫面操作時你可能會發現一些 bug,入下圖所示
我先按下上面的 +1按鈕 ,你會發現兩個 Controller 的 count 都會變 2,這是正確的
再按下下面的+1按鈕,同樣兩個直都會變 3,這也沒什麼問題
之後按下下面的 -1 按鈕, Child count 的直就會變成 2,但是上面的 Parent count 並沒有變
2,這是有問題的,因為 childCtrl1 的 $scope.count 是繼承於 parentCtrl1 的 count ,他們應該是同一個 Reference 才對
這時候再按下 任何一個 +1 按鈕,你會發現下面的 Child count 永遠不會被加1了
一開始遇到還以為是程式寫錯或是 AngularJS 的臭蟲,但其實這是正確的,
先來探討這個問題如何產生的,我們知道所有問題都是再按下 -1 的按鈕之後所造成的
其實 childCtrl1 的 minus function 並沒有什麼問題,問題再於
$scope.count 之上
首先 childCtrl1 並沒有在他的 scope 內定義 count,他是使用(reuse) parentCtrl 的 scope count
這沒錯,畢竟我的目的就是要在這兩個不同的 view 上顯示同一個資料,沒必要分開定義
問題是在於 當我出發 mins function 的時候,因為 childCtrl1 的 scope 並沒有宣告 count
因此他在執行
$scope.count--; 的時候,會預設幫你在這個 childCtrl1 的 scope 定義一個 count
因此這時候兩個 Controller 的 count 就分別獨立了,因此不管你按了哪個 +1按鈕,child count 永遠都不會動了
該如何解決,你只要如下更改程式即可
<script>
var
demoApp = angular.module("demoApp", []);
demoApp.controller("parentCtrl1", function($scope){
$scope.data = {
count:
1
};
$scope.add = function(){
$scope.data.count++;
};
}).controller("childCtrl1", function($scope){
$scope.minus = function(){
$scope.data.count--;
};
});
</script>
<div ng-controller="parentCtrl1">
<p>Parent
count: {{data.count}}</p>
<button ng-click="add()">+1</button>
<div ng-controller="childCtrl1">
<p>Child
count: {{data.count}}</p>
<button ng-click="add()">+1</button><button ng-click="minus()">-1</button>
</div>
</div>
可以注意到粗體部分,我只將換了一個宣告方式,我不直接在 scope 內定義一個 count 屬性
而是在 scope 內定義一個物件,而物件內才有 count 屬性!!
這樣就解決所有問題了....QQ
為什麼會這樣,其實這是因為 Javascipt 的繼承機制造成的(prototype inheritance),
相信對 Javascript 有一定了解的都會聽過 ~
至於實際的原理就不多說了,因為那又是另一個很深度的話題了