我准备了一个小提琴并将其煮至最低限度:
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:
不再反映在标签中。
我阅读了有关指令,范围和转换的角度文档,但无法找到对此不良行为的解释。
我会很感激任何提示:-)
当您编辑基元时,问题与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
该 <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
父对象,而不是被转换的子范围。
问题在于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中。