Javascript 双向绑定在具有嵌入范围的指令中不起作用
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14481610/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Two way binding not working in directive with transcluded scope
提问by Uzair Farooq
I've a textbox in a controller which is bound to model name. There's a directive inside the controller and there's another textbox inside the directive which is bound to the same model name:
我在绑定到 model 的控制器中有一个文本框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>',
};
});
When you type something in the outer textbox it's reflected in the inner textbox but if you type something in the inner textbox it stops working i.e. both textbox no more reflects the same value.
当您在外部文本框中键入内容时,它会反映在内部文本框中,但是如果您在内部文本框中键入内容,它就会停止工作,即两个文本框不再反映相同的值。
See example at: http://jsfiddle.net/uzairfarooq/MNBLd/
参见示例:http: //jsfiddle.net/uzairfaooq/MNBLd/
I've also tried using two way binding attr (scope: {name: '='}) but it gives syntax error.And using scope: {name: '@'}has same effect.
我也试过使用两种方式绑定 attr ( scope: {name: '='}) 但它给出了语法错误。使用scope: {name: '@'}具有相同的效果。
Any help would be greatly appreciated.
任何帮助将不胜感激。
In addition to the accepted answer, this articlereally helped me in understanding the prototypical inheritance in child scpoes. I'd highly recommend anyone having problem with scopes to read it thoroughly.
除了公认的答案之外,这篇文章确实帮助我理解了 child spoes 中的原型继承。我强烈建议任何对范围有问题的人彻底阅读它。
回答by Mark Rajcok
A directive with transclude: trueresults in the directive creating a new (transcluded) child scope. This new scope prototypically inherits from the parent scope. In your case, the parent scope is the scope associated with the editCtrl controller.
指令transclude: true导致指令创建一个新的(被嵌入的)子作用域。这个新的作用域原型继承自父作用域。在您的情况下,父作用域是与 editCtrl 控制器关联的作用域。
Using two-way databinding in a child scope (i.e., ng-model) to bind to a parent scope property that holds a primitive value (e.g., name) always causes problems -- well, I should say that it doesn't work as expected. When the scope property is changed in the child (e.g., you type into the second textbox) the child creates a new scope property that hides/shadows the parent scope property of the same name. If the parent property holds a primitive value, that value is (essentially) copied to the child property when the child property is created. Future changes in the child scope (e.g., the second textbox) only affect the child property.
在子作用域(即 ng-model)中使用双向数据绑定来绑定到包含原始值(例如name)的父作用域属性总是会导致问题——好吧,我应该说它没有按预期工作. 当子级中的范围属性发生更改时(例如,您在第二个文本框中键入),子级会创建一个新的范围属性,该属性隐藏/隐藏同名的父级范围属性。如果父属性持有原始值,则在创建子属性时,该值(本质上)被复制到子属性。子范围(例如,第二个文本框)的未来更改仅影响子属性。
Before typing into the second textbox (i.e., before the property is changed in the child), the child/transcluded scope finds the nameproperty in the parent scope via prototypal inheritance (dashed line in picture below). This is why the two textboxes initially remain in synch. Below, if you type "Mark" into the first text box, this is what the scopes look like:
在输入第二个文本框之前(即在子域中更改属性之前),子域/嵌入域name通过原型继承(下图中的虚线)在父域中查找属性。这就是为什么两个文本框最初保持同步的原因。下面,如果您在第一个文本框中键入“Mark”,则范围如下所示:


I created a fiddlewhere you can examine the two scopes. Click the "show scope" link next to the second textbox before typing into the second textbox. This will allow you to see the transcluded child scope. You will notice that it does not have a nameproperty at this point. Clear the console, type into the second text box, then click the link again. You will notice that the child scope now has a nameproperty, and the initial value was the value that parent property had ("Mark"). If you typed " likes Angular" into the second text box, this is what the scopes look like:
我创建了一个小提琴,您可以在其中检查两个范围。在输入第二个文本框之前,单击第二个文本框旁边的“显示范围”链接。这将允许您查看嵌入的子范围。您会注意到此时它没有name属性。清除控制台,在第二个文本框中键入,然后再次单击该链接。您会注意到子作用域现在有一个name属性,初始值是父属性的值(“Mark”)。如果你在第二个文本框中输入“likes Angular”,作用域是这样的:


There are two solutions:
有两种解决方案:
- do what @pgreen2 suggests (this is the "best practice" solution) -- use an object instead of a primitive. When an object is used, the child/transcluded scope does not get a new property. Only prototypal inheritance is in play here. In the picture below, assume the editCtrl's $scope has this object defined:
$scope.myObject = { name: "Mark", anotherProp: ... }:
- use $parent in the child scope (this is a fragile solution, and not recommended, as it makes assumptions about HTML structure): use
ng-model="$parent.name"inside the <input> that is within the <tabs> element. The first picture above shows how this works.
- 做@pgreen2 建议的事情(这是“最佳实践”解决方案)——使用对象而不是原语。使用对象时,子/嵌入范围不会获得新属性。这里只有原型继承在起作用。在下图中,假设 editCtrl 的 $scope 定义了这个对象
$scope.myObject = { name: "Mark", anotherProp: ... }:
- 在子作用域中使用 $parent (这是一个脆弱的解决方案,不推荐,因为它对 HTML 结构做出了假设):
ng-model="$parent.name"在 <tabs> 元素内的 <input> 内使用。上面的第一张图片显示了这是如何工作的。
A syntax error occurs when using scope: {name: '='}because when using two-way databinding (i.e., when using '='), interpolation is not allowed -- i.e., {{}} can't be used. Instead of <tabs name="{{name}}">use <tabs name="name">.
使用scope: {name: '='}时会出现语法错误,因为在使用双向数据绑定时(即使用“=”时),不允许插值——即不能使用 {{}}。而不是<tabs name="{{name}}">使用<tabs name="name">。
Using '@' works the same as the transclude case because ng-transclude uses the transcluded scope, not the isolate scope that is created by using scope: { ... }.
使用 '@' 与 transclude 情况相同,因为 ng-transclude 使用的是 transcluded 范围,而不是使用创建的隔离范围scope: { ... }。
For (lots) more information about scopes (including pictures) see
What are the nuances of scope prototypal / prototypical inheritance in AngularJS?
有关范围(包括图片)的(大量)更多信息,请参阅
AngularJS 中范围原型/原型继承的细微差别是什么?
回答by pgreen2
I believe that the problem has to do with scoping. Initially the inner textbox doesn't have nameset, so it is inherited from the outer scope. This is why typing in the outer box is reflected in the inner box. However, once typing in the inner box occurs, the inner scope now contains namewhich means it is no longer bound to the outer nameso the outer text box doesn't sync up.
我认为问题与范围界定有关。最初内部文本框没有name设置,所以它是从外部范围继承的。这就是为什么在外框中打字会反映在内框中的原因。但是,一旦在内部框中键入,内部范围现在包含name,这意味着它不再绑定到外部,name因此外部文本框不会同步。
The appropriate way to fix is only storing models in the scope, not your values. I fixed it in http://jsfiddle.net/pdgreen/5RVza/The trick is to create a model object (data) and referencing values on it.
正确的修复方法是仅在范围中存储模型,而不是您的值。我在http://jsfiddle.net/pdgreen/5RVza/ 中修复了它 。诀窍是创建一个模型对象 ( data) 并在其上引用值。
The incorrect code modifies the scope in the directive, the correct code modifies the model in the scope in the directive. This subtle difference allows the scope inheritance to work properly.
不正确的代码修改指令中的范围,正确的代码修改指令中范围内的模型。这种细微的差别允许范围继承正常工作。
I believe the way Mi?ko Hevery phrased it was, scope should be write-onlyin the controller, and read-onlyin directives.
我相信 Mi?ko Hevery 的说法是,作用域在控制器中应该是只写的,而在指令中应该是只读的。
update: reference: https://www.youtube.com/watch?v=ZhfUv0spHCY#t=29m19s
回答by Umur Kontac?
Syntax error means that you miswrote something. It is not related to a particular framework / library. You probably forgot to add "," or close a paranthesis. Check it out again
语法错误意味着你写错了一些东西。它与特定的框架/库无关。您可能忘记添加“,”或关闭括号。再看看

