2013-02-19 39 views
11

我備一點小提琴和煮下來到最小:角引導(標籤):數據綁定的工作方式只能單向

http://jsfiddle.net/lpeterse/NdhjD/4/

<script type="text/javascript"> 
    angular.module('app', ['ui.bootstrap']); 

    function Ctrl($scope) { 
     $scope.foo = "42"; 
} 
</script> 


<div ng-app="app" ng-controller="Ctrl"> 
    1: {{foo}}<br /> 
    2: <input ng-model="foo" /> 
    <tabs> 
     <pane heading="tab"> 
      3: {{foo}}<br /> 
      4: <input ng-model="foo" /> 
     </pane> 
    </tabs>  
</div> 

在開始的時候所有視圖的參考模型Ctrl.foo

如果您更改輸入2:中的內容,它會正確更新模型,並將此更改傳播到所有視圖。

更改輸入4:中的內容僅影響包含在同一窗格中的視圖。它的行爲就像分叉的範圍。之後從2:更改不會反映在標籤中。

我閱讀了有關指令,範圍和變形的角度文檔,但無法找到這種不希望的行爲的解釋。

我願意爲任何提示感激:-)

+0

JSFiddle目前正在關閉。無論如何,您應該在問題中包含任何相關的代碼。 – 2013-02-19 15:50:20

回答

12

的問題是一樣的NG-重複,當你編輯原始的 - <pane>指令創建一個新的範圍,其繼承父。

現在,考慮到Javascript繼承的工作方式,<pane>指令有自己的foo字符串原語的副本,當您編輯它時,您只是在窗格的子範圍上編輯它。

簡單的解決辦法是把foo一對象父Ctrl鍵:

function Ctrl($scope) { 
    $scope.data = { foo: 42 }; 
} 

然後你就可以在你的HTML做到這一點:

<tabs><pane><input ng-model="data.foo"></pane></tabs> 

爲什麼它與對象工作?因爲當<pane>繼承父級作用域時,其對data的引用將引用內存中與父級Ctrl相同的對象。像字符串和數字這樣的基元被複制到繼承中,對象只是創建一個指向同一對象的新指針。

TL; DR:<pane>的新範圍繼承foo串作爲原始的foo一個新副本,其編輯不會對父Ctrl鍵改變時。 <pane>的新範圍繼承像data一個對象作爲同一對象的引用,並且在<pane>範圍編輯時相同的對象將在母範圍被引用。

有用的文章:https://github.com/angular/angular.js/wiki/The-Nuances-of-Scope-Prototypal-Inheritance

+0

好的,正如你解釋的那樣,這種感覺很有意義。我並沒有預料到新的範圍從字面上複製了派生的範圍中的元素,當然這導致了對於基元的這種行爲。 仍然非常令人困惑的是,新的範圍是在第一次寫入時創建的。在此之前,這些觀點仍然直接反映了原來的範圍。 tl; dr:不要使用原語:-) – 2013-02-19 16:28:31

+0

@Lars,創建子作用域時不會複製原語。請參閱我的回答,以獲取更多有關何時/如何將父範圍基元值「複製」到子範圍的說明。 – 2013-02-28 17:30:28

+0

這爲我排序! – 2014-01-02 12:00:52

0

問題就出在標籤指令。我想在line 1044 of ui-bootstrap-tpls-0.1.0.js

如果將scope: {}更改爲scope: { foo: '='}它應該爲您提供雙向數據綁定。

Angular Docs

=或= ATTR - 建立本地範圍屬性,並經由ATTR屬性的值定義的名稱的父範圍屬性之間的雙向綁定。如果未指定attr名稱,則假定屬性名稱與本地名稱相同。給定範圍的小部件定義:{localModel:'= myAttr'},那麼小部件範圍屬性localModel將反映父範圍上parentModel的值。對parentModel的任何更改都會反映在localModel中,localModel中的任何更改都會反映在parentModel中。

4

<tabs><pane>指令各創建一個新的transcluded子範圍(因爲它們都具有transclude: true,),這prototypically從父範圍繼承和隔離子範圍,其不prototypically繼承父範圍。 <pane>中的<input...>使用transcluded子範圍。

當第一次呈現<pane>內部的input時,它將填充值爲$scope.foo。 正常的JavaScript原型繼承在這裏發揮作用...最初foo而不是定義在transcluded子範圍(原型繼承不復制基元),所以JavaScript遵循原型鏈並查看父對象/ $ scope,並且在那裏找到它。將42放入文本框中。跨越的子範圍不受影響/更改(尚未)。

如果您編輯第一個文本框,則會更新第二個文本框,因爲JavaScript仍在使用原型繼承來查找$scope.foo的值。

如果您編輯第二個文本框,說429,Angular將值寫入$scope.foo,但請注意$ scope是transcluded子範圍。由於foo是一個原始類型,因此它會在該子範圍上創建一個新屬性 - 這就是JavaScript的工作方式,無論更好還是更糟。這個新屬性將會隱藏/隱藏同名的父範圍屬性。原型繼承不在這裏玩。 (本文安迪提到了他的崗位(這也是on SO)也說明對此進行了詳細,有圖片。)由於transcluded子範圍現在有一個foo屬性,它現在將使用本地地產閱讀和寫作,所以纔出現從父範圍「斷開」。

使用對象(而不是原始的),因爲原型繼承的則始終發揮解決了這個問題。移過的子範圍獲取對父範圍中的對象的引用。寫入data.foo會寫入父對象上的data對象,而不會寫入被跨行的子範圍。