2022年9月18日 星期日

在同一個頁面上建立多個 AngularJs 的 module 及 controller - 使用 angular.bootstrap()

ng-app 是很常見的 AngularJs 頁面 module 的設定,
但這樣的方式會有局限性,
因為 AngularJs 只允許一個頁面使用一個 ng-app,
如果有多個 ng-app 則會錯誤或不正常運作。

不過這並不代表 AngularJs 不能在一個頁面上設定多個 module,
AngularJs 可以使用
angular.bootstrap(element, moduleNameArray)
來在一個頁面上設定多個 module,
其中參數 element 是 module 所在的 DOM Object,
而 moduleNameArray 則是個 array<String>,
內部放著要設定到頁面上的 module 名稱。

以下是一個範例,
頁面上有三個要被設定 module 的 DOM Object,
分別是 id="module_1" 、 id="module_2" 和 id="module_3",
然後我們會先設定兩個  AngularJs module,module 名為 "module_A" 和 "module_B",
各 module 裡面各有一個名為 "controller" 的 controller,
我們要把 module_A 設定到 #module_1 和 #module_2 上,
及把 module_A 和 module_B 一起設定到 #module_3 上。

html:
<div id="module_1">
  module_1:
  <div ng-controller="controller as ctrl">
    <input type="text" ng-model="ctrl.text"/>
  </div>
</div>

<div id="module_2">
  module_2:
  <div ng-controller="controller as ctrl">
    <input type="text" ng-model="ctrl.text"/>
  </div>
  
   <div ng-controller="controller as ctrl">
    <input type="text" ng-model="ctrl.text"/>
  </div>
</div>

<div id="module_3">
  module_3:
  <div ng-controller="controller as ctrl">
    <input type="text" ng-model="ctrl.text"/>
  </div>
  
  <div ng-controller="controller as ctrl">
    <input type="text" ng-model="ctrl.text"/>
  </div>
</div>
Javascript:
angular.module("module_A", [])
.controller("controller", [function(){
	var self = this;
  self.text = "A";
}])

angular.module("module_B", [])
.controller("controller", [function(){
	var self = this;
  self.text = "B";
}]);

angular.bootstrap(document.getElementById("module_1"), ["module_A"]);
angular.bootstrap(document.getElementById("module_2"), ["module_A"]);
angular.bootstrap(document.getElementById("module_3"), ["module_A", "module_B"]);
以下是 Jsfiddle 上的成品:
說明:
首先可以先注意到,在 html 中的
id="module_2" 中
故意擺放了名稱一樣的 controller:
ng-controller="controller as ctrl"
這裡是要展示一個特性,就是即使同一個 module 下有相同 controller 名稱的 controller,
也就是它們都使用了在 javascript 中同一個 module 之下的同樣名為 "controller" 的 controller 設定,
它們的 scope 仍然是不同的,方便想像可以把它們看做是同一個 function 所 new 出來的不同  Class 實體 (Class Instance),
所以它們設定的 ng-model 是各自獨立的,並不會互相連動,
可以從 Jsfiddle 上的成品範例實際使用觀察看看。

再來在 html 中的
id="module_1" 和 id="module_2" 雖然都用了在 javascript 中相同的名為 "module_A" 的 module 設定,
不過它們仍然算是互相獨立的不同 module 個體,跟之前同個 module 下的同名 controller 說明很像,
所以它們彼此的同名 controller 的 ng-model 也是不會互相連動的。

最後是在 html 中的
id="module_3",它被設定了 module_A 和 module_B,
所以它可以得到 module_A 之下及 module_B 之下的 controller 設定,
有點像是一個載入了 module_A 和 module_B 的 module_C 一樣,
可以方便想像成如下:
angular.module("module_C", ["module_A", "module_B"]),
然後當然的,跟上面說明的情況一樣,
id="module_3" 上設定的 module 跟 controller 一樣是獨立於 id="module_1" 和 id="module_2",
事實上,id="module_1"、id="module_2" 和 id="module_3" 上的 module 跟 controller 彼此都是互相獨立無關的,
唯一的共同點就只有都有使用到了 module_A 的設定而已。

最後的結果是範例中四個 <input> 的值彼此都不是互相連動的。
這在如果你想要把相同程式邏輯的 module 做成元件,
並放到頁面上的各處,又不想它們彼此共用 scope (即共用 ng-model 去連動改變 scope 中的變數值) 時,
會是一個可利用的不錯的特性。

是 Luis 作者寫的一篇關於 ng-app 限制的文章,
也一樣用到了 angular.bootstrap 去解決 ng-app 限制的問題,
並在 Github 上實作了一個方便來用 DOM 屬性設定 AngularJs module 的 module 工具,
源碼部份也是使用了一樣的觀念,
找出要設定 module 的 DOM,從屬性中讀到要被設定的 module 名稱後進行 module 的設定,
十分地具有參考的價值。

沒有留言 :

張貼留言