双向绑定不在指令中与transcluded范围一起工作

时间:2013-01-23 14:04:36

标签: javascript angularjs

我在控制器中有一个绑定到模型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: '@'}会产生同样的效果。

非常感谢任何帮助。

除了接受的答案外,this article确实帮助我理解了儿童scpoes的原型继承。我强烈建议任何有范围问题的人仔细阅读。

3 个答案:

答案 0 :(得分:128)

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

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

在键入第二个文本框之前(即,在子项中更改属性之前),子/ transcluded范围通过原型继承(下图中的虚线)在父作用域中找到name属性。这就是为什么两个文本框最初保持同步。下面,如果在第一个文本框中键入“Mark”,则这是范围的样子:

transcluded scope follows inheritance chain

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

transcluded primitive hides parent property

有两种解决方案:

  1. 做@ pgreen2建议的内容(这是“最佳实践”解决方案) - 使用对象而不是基元。使用对象时,子/ transcluded范围不会获得新属性。这里只有原型继承。假设editCtrl的$ scope定义了这个对象:
    $scope.myObject = { name: "Mark", anotherProp: ... }
    object in parent
  2. 在子范围中使用$ parent(这是一个脆弱的解决方案,不建议使用,因为它会对HTML结构做出假设):在&lt; input&gt;中使用ng-model="$parent.name"在&lt; tabs&gt;内元件。上面的第一张图片展示了它是如何工作的。
  3. 使用scope: {name: '='}时会出现语法错误,因为在使用双向数据绑定时(即使用'='时),不允许插值 - 即不能使用{{}}。而不是<tabs name="{{name}}">使用<tabs name="name">

    使用'@'与transclude案例的工作原理相同,因为ng-transclude使用了被转换的范围,而不是使用scope: { ... }创建的隔离范围。

    关于(大量)有关范围(包括图片)的更多信息,请参见
    What are the nuances of scope prototypal / prototypical inheritance in AngularJS?

答案 1 :(得分:10)

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

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

错误的代码修改了指令中的范围,正确的代码修改了指令范围内的模型。这种微妙的差异允许范围继承正常工作。

我相信MiškoHevery所说的方式,范围应该是控制器中的只写,并且指令中的只读

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

答案 2 :(得分:0)

语法错误意味着你错误地写了一些东西。它与特定框架/库无关。您可能忘了添加“,”或关闭一个paranthesis。再看一遍