2014年6月24日 星期二

AngularJS 教學 - Controller 進階(1)

上一篇介紹了基本的 Controller 與 View 之間的應用 AngularJS 教學 - Controller and View

這篇來進一步介紹 Controller 內部吧,其實 Controller 相較於 Model 或是 Directives

都還要簡單許多,就目地面來說的確如此,畢竟 Controller 就只是個扮演

Model 與 View 之間的連結角色,提供資料以及服務給 View,並且處理一些 user action

但要掌握一整個 MVC 架構,Controller 仍然是一個關鍵的因素

以下先用一個類似於上一篇的範例來說明起:



<!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("demoCtrl", function($scope){
                  $scope.name = "Allen";
                 
                  $scope.getAge = function(name){
                       if(name == "Allen") return 25;
                       else if(name == "John") return 30;
                       else return 0;
                  };
              });
         </script>
     </head>
     <body>
         <div ng-controller="demoCtrl">
              Name: {{name}}
              <br/>
              Age: {{getAge(name)}}
         </div>
     </body>

</html>

上一篇說明到 $scope 的用途,可以注意到的是 我在 $scope 內定義了一個 getAge 函式

需要傳入名子才會回傳年齡,可以注意在 View 之中是怎呼叫的

直接在 Expression 內呼叫 getAge 函式,因為這個 Expression 是被 demoCtrl 所控管的

所以 AngularJS 會直接呼叫 demoCtrl 的 $scope 的 getAge 方法。

另外值得一提的是如果沒有在 $scope 內定義 getAge 也不會發生任何錯誤

頁面上也不會呈現 null 之類的,AngularJS 都會處理掉。

接下來說明一些比較關於設計及進階的問題


該如何定義一個 Controller 的範圍?

        在一些簡單的教學中,可能會直接將 ng-controller 應用在 body 標籤

        這其實就間接表示了這份HTML幾乎都由該 Controller 所負責

        當然簡單的專案你可以這樣做,但是當你的視圖與邏輯越來越複雜之後

        Controller 的責任就越來越多,且更難以維護,

        因此基於單一職責原則以及模組化概念,最好還是將 Controller 依照視圖去切分


一個 View 可否重覆使用同一個 Controller?

        答案是可以的,但是要注意的是

        demoApp.controller("demoCtrl"function($scope){
           $scope.name = "Allen";
                 
           $scope.getAge = function(name){
                if(name == "Allen"return 25;
                else if(name == "John"return 30;
                else return 0;
           };
     });

        上述的橘色 function 會被執行兩次 (如果你在視圖上定義兩次的話)

        其實從 AngularJS 的官方文件可以得知 橘色那段 function 是一個 Factory Function

        顧名思義就是,當需要使用一個 Controller 的時候就會執行這段 function

        並且你會發現,兩個 Controller 的 $scope 的資料是並不會互通互相影響的

        總之就是兩個 Controller 各自負責自己所控管的 View,互不干擾。例如:
        <script>
          var demoApp = angular.module("demoApp", []);
          demoApp.controller("demoCtrl1", function($scope){
              $scope.count = 1;
              $scope.add = function(){
                   $scope.count++;
              };
          });
     </script>
        <div ng-controller="demoCtrl1">
         {{count}}
         <button ng-click="add()">Increment</button>
     </div>

     <div ng-controller="demoCtrl1">
         {{count}}
     </div>

        demoCtrl1 分別控制兩個不相干的 div,如果按下上面那個 button 只會增加

        上面的 $scope.count 的值,下面的 $scope.count 的值並不會加1

Controller 可以巢狀(nested)表示嗎 ?

        其實這我也不太會描述,官方說法是 "Scope Inheritance",如下

  <div ng-controller="demoCtrl">
         {{name}}
         <div ng-controller="demoCtrl2">
              {{name}}
              <div ng-controller="demoCtrl3">
                  {{name}}
              </div>
          </div>
     </div>

         簡單說就是 Controller 所控管的 View 底下又有節點被其他的 Controller 控管

         我們看一下程式的片段

        <script>
          var demoApp = angular.module("demoApp", []);
          demoApp.controller("demoCtrl1", function($scope){
              $scope.name = "Controller one";
          }).controller("demoCtrl2", function($scope){
              $scope.name = "Controller two";
          }).controller("demoCtrl3", function($scope){
              $scope.name = "Controller three";
          });
     </script>

         在這種情況下,三個 Controller 的 $scope 都有定義 name 這個資料屬性

         在 AngularJS 視為是繼承的關係:demoCtrl3 繼承 demoCtrl2,demoCtrl2 繼承 demoCtrl1

         所以最後視圖呈現出來的結果,三個 name 的值都會不一樣,

         因為 demoCtrl2 的 $scope.name 覆寫(override)掉了 demoCtrl1 的 $scope.name

         而 demoCtrl3 則是覆寫掉了 demoCtrl2 的,簡言之在這種巢狀的情況下

         就是一個繼承的概念,所有你所知的繼承觀念都會實現。


Controller 之間可以互相溝通嗎?
       
        可以,透過 $rootScope 來達成,回到上上一個例子

        如何按下上面那個 button 之後,下面的 Controller 的 $scope.count 能夠加1?

        <script>
          var demoApp = angular.module("demoApp", []);
          demoApp.controller("demoCtrl1", function($rootScope, $scope){
              $scope.count = 1;
              $scope.add = function(){
                   $scope.count++;
                   $rootScope.$broadcast("countUpdate", $scope.count);
              };
              $scope.$on("countUpdate", function (event, param) {
               $scope.count = param;
           });
          });
     </script>       

        首先當聽到 button 按下並觸發 add function 時候,利用 $rootScope.$broadcast 

        觸發一個 "countUpdate" 的 Event 並把值當作參數傳入

        並用 $scope.$on 接收這個 Event 並做後續動作,因此 View 不需要更動,

        你會發現按下按鈕後  上下的 count 都會跟著加1了


接下一篇會再更詳細描述更多 Controller 的細節與陷阱

沒有留言:

張貼留言