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


我准备了一个小提琴并将其煮至最低限度:

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

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

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


3104
2018-02-19 15:29


起源

JSFiddle目前正在下调。无论如何,您应该包括与问题相关的任何代码。 - Ben Lesh


答案:


当您编辑基元时,问题与ng-repeat中的问题相同 - <pane> directive创建一个从父级继承的新范围。

现在,考虑到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 string原语作为新的副本 foo 在编辑时不会在父Ctrl上更改。 <pane>的新范围  继承像这样的对象 data 作为对同一对象的引用,以及在编辑时 <pane> 范围将在父范围上引用相同的对象。

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


12
2018-02-19 16:18



好的,正如你解释的那样有道理。我没想到新的作用域会从它派生的作用域中复制元素,这当然会导致基元的这种行为。在第一次写入时创建新范围仍然非常令人困惑。在此之前,观点仍然直接反映原始范围。 tl; dr:不要使用原语:-) - Lars Petersen
@Lars,创建子范围时不复制基元。有关父范围原始值何时/如何“复制”到子范围的更多说明,请参阅我的答案。 - Mark Rajcok
这为我排序了! - Willshaw Media


<tabs> 和 <pane> 每个指令都创建一个新的transcluded子范围(因为它们都有 transclude: true,)原型继承自父作用域,以及不从父作用域原型继承的隔离子作用域。该 <input...> 在 - 的里面 <pane> 使用transcluded子范围。

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

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

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

使用对象(而不是原始对象)可以解决问题,因为原型继承总是在起作用。 transcluded子作用域获取对父作用域中对象的引用。写给 data.foo 写到 data 父对象,而不是被转换的子范围。


4
2018-02-28 17:27





问题在于tabs指令。我想 ui-bootstrap-tpls-0.1.0.js的第1044行

如果你改变了 scope: {} 至 scope: { foo: '='} 它应该给你一个双向数据绑定。

Angular Docs

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


0
2018-02-19 16:10