2014年9月18日 星期四

[AngularJS] Scope issues when using custom directive (用客製 directive 的 scope 注意事項)



當使用 AngularJS 客製化的 HTML 標籤 (Directive) 時,
要特別注意 Scope,下面敘述五種不同設定下的 Scope 差別。

預備知識

scope 和 transclude 的預設值皆為 false。



1. Directives without attributes 'scope' and 'transclude'

這種情況下,directive 的 scope 就是 parent scope,
沒有新建 scope,正常綁定(bind)沒有問題。

但很危險的是,若 directive 要可以重複使用,
因為都是使用 parent scope 的變數,很容易會變更到同一個變數,
而使結果不如預期。

2. Directives with attribute 'scope: true'

Ex: ng-if, ng-include, ng-switch, ng-controller, ng-template, ...etc

這種情況下,directive 會新建一個 child scope 繼承 parent scope。

當改變 primitive 或者物件的值時,會在 child scope 建立一個新的同名稱變數,
而非使用原本 parent scope 的變數,例如:
<accordion>
  <accordion-group>
    <accordion-heading> ... </accordion-heading>
    <input type="text" ng-model="name" />
  </accordion-group>
</accordion>
name 在 parent scope 裡面是字串型態的變數,
在 input 中改變值時,並不會改變到 parent scope 的 name 變數,

解決方式
<accordion>
  <accordion-group>
    <accordion-heading> ... </accordion-heading>
    <input type="text" ng-model="$parent.$parent.name" />
  </accordion-group>
</accordion>
這是最容易混淆的地方。

當改變物件內屬性的值時,則會如預期般,改變 parent scope 的變數的值。

3. Directives with attribute 'scope: {...}'

這種情況下,directive 的 child scope 是一個 isolate scope,
無法存取到 parent scope 的東西,
要用 '=' 、 '@' 、 '&' 和標籤的屬性設定才能做雙向、單向或陳述和 parent scope 的綁定(bind)。

雖然新建的 child scope 不繼承 parent scope,
但還是可以透過 $parent 取得 parent scope 的東西,
所以還是算 child scope。

4. Directives with attribute 'transclude: true'

這種情況下,directive 會新建一個繼承 parent scope 的 transcluded child scope,
情況跟第二種相同,
如果 directive 裡面有 isolate scope,兩者是 sibling 的關係,
彼此不相關,只是有同一個 parent scope。

5. ng-repeat 等迴圈式、重複式 directives

這種情況下,會根據 element 數量建立出多少個 child scopes,
而 elements 在每一個自己的 child scope 內複製一份新的 element,
所以在使用 ng-repeat 時,如果在裡面去更動 element 的值,
並不會更動到 parent scope 陣列裡面的 element 的值。

解決方式

把 elements 從 primitives (studentName) 或物件 (student) 改為物件的屬性 (student.name) 。

參考

原始資料 (附圖示!) 可以參考 Understanding Scopes 。

不過最原始的資料是從 這邊 來的。

P.S.

這概念跟設定 nameA = nameB 類似。


2 則留言: