2014年6月25日 星期三

AngularJS 教學 - Controller 進階(2)

上一篇我們延續了 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 有一定了解的都會聽過 ~

至於實際的原理就不多說了,因為那又是另一個很深度的話題了


沒有留言:

張貼留言