58

我在绑定到模型的控制器中有一个文本框name。控制器中有一个指令,指令中有另一个文本框,它绑定到同一个模型name

<div class="border" ng-controller="editCtrl">
   Controller: editCtrl <br/>
   <input type="text" ng-model="name" />
   <br/>
   <tabs>
      Directive: tabs <br/>
      <input type="text" ng-model="name"/>
   </tabs>
</div>

mod.directive('tabs', function() {
  return {
    restrict: 'E',
    transclude: true, 
    template:
      '<div class="border" ng-transclude></div>',
  };
});

当您在外部文本框中输入内容时,它会反映在内部文本框中,但如果您在内部文本框中输入内容,它将停止工作,即两个文本框不再反映相同的值。

参见示例:http: //jsfiddle.net/uzairfarooq/MNBLd/

我也尝试过使用两种方式绑定 attr ( scope: {name: '='}) 但它给出了语法错误。使用scope: {name: '@'}具有相同的效果。

任何帮助将不胜感激。

除了公认的答案,这篇文章确实帮助我理解了子 scpoes 中的原型继承。我强烈建议任何对范围有问题的人彻底阅读它。

4

3 回答 3

128

一个指令transclude: true会导致指令创建一个新的(转入的)子范围。这个新的作用域原型继承自父作用域。在您的情况下,父范围是与 editCtrl 控制器关联的范围。

在子作用域(即 ng-model)中使用双向数据绑定来绑定到包含原始值(例如name)的父作用域属性总是会导致问题——好吧,我应该说它没有按预期工作. 当子级的范围属性发生变化时(例如,您在第二个文本框中键入),子级会创建一个新的范围属性来隐藏/隐藏同名的父级范围属性。如果父属性包含原始值,则在创建子属性时该值(本质上)被复制到子属性。子范围(例如,第二个文本框)的未来更改仅影响子属性。

在输入第二个文本框之前(即,在子属性中更改之前),子/嵌入name范围通过原型继承在父范围中找到属性(下图中的虚线)。这就是两个文本框最初保持同步的原因。下面,如果您在第一个文本框中键入“标记”,则范围如下所示:

嵌入范围遵循继承链

我创建了一个小提琴,您可以在其中检查两个范围。在输入第二个文本框之前,单击第二个文本框旁边的“显示范围”链接。这将允许您查看嵌入的子范围。您会注意到此时它没有name属性。清除控制台,在第二个文本框中键入,然后再次单击该链接。您会注意到子范围现在有一个name属性,初始值是父属性的值(“Mark”)。如果您在第二个文本框中键入“likes Angular”,则范围如下所示:

嵌入原语隐藏父属性

有两种解决方案:

  1. 做@pgreen2 建议的事情(这是“最佳实践”解决方案)——使用对象而不是原语。使用对象时,子/嵌入范围不会获得新属性。这里只有原型继承在起作用。在下图中,假设 editCtrl 的 $scope 定义了这个对象
    $scope.myObject = { name: "Mark", anotherProp: ... }
    父对象
  2. 在子范围内使用 $parent (这是一个脆弱的解决方案,不推荐,因为它对 HTML 结构做出假设):ng-model="$parent.name"在 <tabs> 元素内的 <input> 内使用。上面的第一张图片显示了它是如何工作的。

使用时会出现语法错误,scope: {name: '='}因为在使用双向数据绑定时(即使用'=' 时),不允许插值- 即不能使用{{}}。而不是<tabs name="{{name}}">使用<tabs name="name">.

使用 '@' 与 transclude 情况相同,因为 ng-transclude 使用的是 transcluded 范围,而不是使用创建的隔离范围scope: { ... }

有关范围(包括图片)的(大量)更多信息,请参阅
AngularJS 中范围原型/原型继承的细微差别是什么?

于 2013-01-23T16:46:54.110 回答
10

我相信这个问题与范围界定有关。最初内部文本框没有name设置,因此它是从外部范围继承的。这就是为什么在外框中输入会反映在内框中的原因。但是,一旦在内部框中键入内容,内部范围现在包含name,这意味着它不再绑定到外部name,因此外部文本框不会同步。

修复的适当方法是仅将模型存储在范围内,而不是您的值。我在http://jsfiddle.net/pdgreen/5RVza/中修复了它, 诀窍是创建一个模型对象 ( data) 并在其上引用值。

不正确的代码会修改指令中的范围,正确的代码会修改指令范围内的模型。这种细微的差别使作用域继承能够正常工作。

我相信 Miško Hevery 的表述方式是,范围在控制器中应该是只写的,而在指令 中应该是只读的。

更新:参考:https ://www.youtube.com/watch?v=ZhfUv0spHCY#t=29m19s

于 2013-01-23T14:16:06.673 回答
0

语法错误意味着你写错了一些东西。它与特定的框架/库无关。您可能忘记添加“,”或关闭括号。再次检查

于 2013-01-23T14:11:48.117 回答