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:不再反映在选项卡中。

我阅读了有关指令、范围和嵌入的角度文档,但找不到对这种不良行为的解释。

我将不胜感激任何提示:-)

4

3 回答 3

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

于 2013-02-19T16:18:07.177 回答
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 在他的帖子中提到的文章(也在SO 上)也详细解释了这一点,并配有图片。)由于嵌入的子范围现在有一个foo属性,它现在将使用该本地属性进行读写,所以它出现了从父范围“断开”。

使用对象(而不是原语)解决了这个问题,因为原型继承总是在起作用。被嵌入的子作用域获得对父作用域中对象的引用。写入data.foo写入data父对象上的对象,而不是嵌入的子范围。

于 2013-02-28T17:27:28.570 回答
0

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

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

来自Angular 文档

= 或 =attr - 在本地范围属性和通过 attr 属性值定义的 name 的父范围属性之间建立双向绑定。如果未指定 attr 名称,则假定属性名称与本地名称相同。给定范围的widget定义:{localModel:'=myAttr'},那么widget范围属性localModel将在父范围上反映parentModel的值。对 parentModel 的任何更改都将反映在 localModel 中,而对 localModel 的任何更改都将反映在 parentModel 中。

于 2013-02-19T16:10:43.223 回答