angular-bootstrap(tabs):数据绑定仅适用于单向

时间:2013-02-19 15:29:51

标签: angularjs angular-ui angularjs-scope

我准备了一个小小的小提琴,把它煮到最低限度:

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:的更改不会再反映在标签中。

我阅读了关于指令,范围和转换​​的角度文档,但无法找到对此不良行为的解释。

我会很感激任何提示: - )

3 个答案:

答案 0 :(得分:12)

问题与编辑基元时的ng-repeat相同 - <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

答案 1 :(得分:4)

<tabs><pane>指令分别创建一个新的被转换的子范围(因为它们都有transclude: true,)原型继承自父范围,而孤立子范围则没有原型继承自父范围。 <input...>内的<pane>使用了被转换的子范围。

首次呈现input内的<pane>时,会使用$scope.foo的值填充它。 正常的JavaScript原型继承在这里发挥作用...最初foo在被抄纵的子范围上定义(原型继承不复制原语),因此JavaScript遵循原型链并查看父对象/ $ scope,并在那里找到它。 42被放入文本框中。转换的子范围不会受影响/更改(尚未)。

如果编辑第一个文本框,则会更新第二个文本框,因为JavaScript仍在使用原型继承来查找$scope.foo的值。

如果你编辑第二个文本框,说429,Angular会将值写入$scope.foo,但请注意$ scope是被转换的子范围。由于foo是一个原语,它会在该子范围上创建一个新属性 - 这就是JavaScript的工作原理,无论好坏。此新属性将隐藏/隐藏同名的父作用域属性。原型继承在这里没有发挥作用。 (Andy在他的帖子中提到的文章(也是on SO)也用图片详细解释了这一点。)由于被抄换的子范围现在具有foo属性,它现在将使用该属性用于读写,因此它看起来与父作用域“断开连接”。

使用对象(而不是原语)可以解决问题,因为原型继承始终在起作用。 transcluded子范围获取对父范围中对象的引用。写入data.foo写入父级的data对象,而不是写入的子级范围。

答案 2 :(得分:0)

问题出在tabs指令中。我想line 1044 of ui-bootstrap-tpls-0.1.0.js

如果您将scope: {}更改为scope: { foo: '='},则应该为您提供双向数据绑定。

来自Angular Docs

  

= or = attr - 在本地范围属性和通过attr属性的值定义的name的父范围属性之间设置双向绑定。如果未指定attr名称,则假定属性名称与本地名称相同。范围的给定和窗口小部件定义:{localModel:'= myAttr'},然后窗口小部件范围属性localModel将反映父范围上的parentModel的值。对parentModel的任何更改都将反映在localModel中,localModel中的任何更改都将反映在parentModel中。