我准备了一个小小的小提琴,把它煮到最低限度:
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 :(得分: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中。