[AngularJS]探討ngApp的限制與Module的關係

  • 6081
  • 0
  • 2015-08-27

AngularJS

此文章為翻譯文,原文出自SIMPLY GOOD CODE

當你的 AngularJS application 越來越複雜,你將會去探索ngApp directive 兩個大的限制特性:

  1. 在每個頁面中,你只可以使用一個ng-app。
  2. 在一個Html元素中 你只可以關聯到一個module 。


 

結合多個modulem於一個module內

 

換個角度去想,將多個module都引用到一個大的module內,我們在來使用這個module,就可以輕易地使用不同module內的controller,以下的範例將解釋我上述的說法:
 

<!DOCTYPE html>
<html>
    <head>
        <script src="angular.js"></script>
        <script>
          var moduleA = angular.module("MyModuleA", []);
          moduleA.controller("MyControllerA", function($scope) {
              $scope.name = "Bob A";
          });

          var moduleB = angular.module("MyModuleB", []);
          moduleB.controller("MyControllerB", function($scope) {
              $scope.name = "Steve B";
          });

          angular.module("CombineModule", ["MyModuleA", "MyModuleB"]);
        </script>
    </head>
    <body ng-app="CombineModule">
        <div>
            <h1>myDiv1</h1>
            <div ng-controller="MyControllerA">
                {{name}}
            </div>
        </div>

        <div>
            <h1>myDiv2</h1>
            <div ng-controller="MyControllerB">
                {{name}}
            </div>
        </div>
    </body>
</html>

雖然這樣可以正常工作,但是這存在一些問題。這樣的描述是不夠清楚和乾淨,div myDiv2 並不須需要使用到 MyControllerB為什麼還需要結合其他的controller。假設在這個html當內,你的module將一直增加,你可以想像這樣的表示方法將讓你的app變得不直觀。

另外這個範例可以正常工作也是因為每個module的controller的名稱不同,如果controller有相同的命名將會發生問題。


現在已經可以利用不同的module來匹配有良好命名的不同HTML元素來使用,這將會帶來更良好的控制。


 

使用angular.bootstrap()達成更加程序式的方法

 

當發現了ngApp不被Angular本身所局限,你可能會感到驚訝。Angular允許你在一個頁面上使用多個module來匹配。Angular也允許你有多個不同的HTML元素在一個頁面上關聯多個module。你只要修改一下語法即可。

以下的範例將演示如何達成,這個範例中有兩個module分別帶有一個controller。myDiv1 關聯了兩個module,而myDiv2關聯一個 module。
 

<!DOCTYPE html>
<html>
    <head>
        <script src="angular.js"></script>
        <script>
          var moduleA = angular.module("MyModuleA", []);
          moduleA.controller("MyControllerA", function($scope) {
              $scope.name = "Bob A";
          });

          var moduleB = angular.module("MyModuleB", []);
          moduleB.controller("MyControllerB", function($scope) {
              $scope.name = "Steve B";
          });

          angular.element(document).ready(function() {
              var myDiv1 = document.getElementById("myDiv1");
              angular.bootstrap(myDiv1, ["MyModuleA", "MyModuleB"]);

              var myDiv2 = document.getElementById("myDiv2");
              angular.bootstrap(myDiv2, ["MyModuleB"]);
          });
        </script>
    </head>
    <body>
        <div id="myDiv1">
            <h1>myDiv1</h1>
            <div ng-controller="MyControllerA">
                {{name}}
            </div>
            <div ng-controller="MyControllerB">
                {{name}}
            </div>
        </div>

        <div id="myDiv2">
            <h1>myDiv2</h1>
            <div ng-controller="MyControllerB">
                {{name}}
            </div>
        </div>
    </body>
</html>

輸出結果:

myDiv1

Bob A
Steve B

myDiv2

Steve B

你可以自己藏試寫上這段代碼或是從以下連結中看出這段代碼演示的代碼和結果: http://jsfiddle.net/luisperezphd/QX3fQ/

這帶給我們很大的靈活度,但是這有個問題,就是程式碼有點醜陋。你必須將html元素寫進你的js代碼內,這意指你與HTML代碼耦合。這樣的作法違背的Angular原本的目地(mvc或mvvm中,view跟 controller或 viewModel 應該是要可以分離的不該耦合)。


 

理想的解決方案

 

對於ngApp理想的解決方案是可以允許任意的使用angular.bootstrap()來允達成;允許你使用在多個HTML element上;允許你指定更多的module。

一般來說這解決方案,我們期望它可以的使用像是Angular directive的方式。

所以問題在於?
你如何定義這個directive?

基於一個可能失敗的目標,你勢必需要使用一個module去定義它。

為了實現一個類似 ngApp directive ,你必須實現我們想像中使用module的方法。我將為你分擔掉這個困擾。我已經實現了它。你可以從這下載 angular.ng-modules.js

這JavaScript檔案內實現了ngModule directive。這個範例告訴你如何改寫以上的程式碼藉由ngModule directive:
 

<!DOCTYPE html>
<html>
    <head>
        <script src="angular.js"></script>
        <script src="angular.ng-modules.js"></script>
        <script>
          var moduleA = angular.module("MyModuleA", []);
          moduleA.controller("MyControllerA", function($scope) {
              $scope.name = "Bob A";
          });

          var moduleB = angular.module("MyModuleB", []);
          moduleB.controller("MyControllerB", function($scope) {
              $scope.name = "Steve B";
          });
        </script>
    </head>
    <body>
        <div ng-modules="MyModuleA, MyModuleB">
            <h1>Module A, B</h1>
            <div ng-controller="MyControllerA">
                {{name}}
            </div>
            <div ng-controller="MyControllerB">
                {{name}}
            </div>
        </div>

        <div ng-module="MyModuleB">
            <h1>Just Module B</h1>
            <div ng-controller="MyControllerB">
                {{name}}
            </div>
        </div>
    </body>
</html>


 

ngModule 說明

 

你可以已經注意到在這個案例中我使用了不同版本的directive,一個是ng-modules可帶多個module,和ng-model只能帶有一個module。
 

這是有意義,你可以指定一個就用ng-module或多個使用ng-modules。我就是喜歡讓程式碼讀起來更直觀。

與Angular的規範使用方法一致,我允許你使用任何的directiv名稱的規則,舉例來說,以下幾個範例皆有效:ng:module, x-ng-modules, data-ng-modules...

最後,你應該了解雖然現在你可以關聯多個HTML element在一個頁面上使用多個module,藉由這個directive。上一節提到的方法,若將HTML element寫入js內,這樣的行為是不洽當的。


 

為什麼要將 ngApp 的侷限

 

這個問題我已經看到在好幾個論壇裡被提出了,但是為什麼Angular 團隊要限制ng-APP。之後我提出剛剛上述的範例中的方案,證明了 Angular 本身對使用多個modules並沒有限制。到目前為止我還沒看過Angular團隊為這個問題提出一個答案。但我大概可以猜測出原因。

先前發布的Angular版本有加入viewrout的功能。事實上,這意味著他們期待並推薦用此方法來使用Angular。若你只擁有一個URL,在同一個頁面上你不可以使用多個views或routes,所以應該已有一些需求是為了可以關聯更多的modules。
 

Angular團隊可能體會到一個Module匹配一個頁面的作法更容易理解及使用。畢竟大家經常聽到Angular 經常提供對於單一頁面應用程式的技術。
 

Angular團隊認為應該更簡單的處理,前兩個範例中也提到解決的方法,表示已存在解決單頁面存在多個module的答案。
 

Angular團隊可能認為處理多個module並存的問題應該存在你的js中,並不應該存在於標記語言。儘管這些爭議性的需求是存在的,我想這個問題應該是最有爭議性的,因為對於一個module來說前端可能只有包含directives。
 

以上所提及不允許於html代碼新增ngAPP以外的module使用。意思是你可以擁有一個module來將所有用到的子module參考近來,這樣一來就能達到目的。Angular團隊可能認為藉由一個module只能存在一個頁面的作法來限制是比較好的做法,如此一來便可以避免html與 js 耦合。



 

by-藍小伙-語言思想....(若有翻得不好的地方 或 思想上的問題 可以指正 或提出! )