我有一个带有隔离作用域的指令(这样我可以在其他地方重用该指令),当我将该指令与ng-repeat一起使用时,它无法工作。
我已阅读了有关此主题的所有文档和堆栈溢出答案,并了解这些问题。我相信我已经避免了所有通常的麻烦。
因此,我理解我的代码由于ng-repeat指令创建的作用域而失败。我自己的指令创建一个隔离作用域,并对父作用域中的对象进行双向数据绑定。我的指令将为这个绑定变量分配一个新的对象值,当我的指令在没有ng-repeat的情况下使用时(父变量被正确更新),这将非常有效。但是,使用ng-repeat时,赋值会在ng-repeat范围内创建一个新变量,父变量看不到更改。所有这一切都是基于我所读到的。
我还了解到,当给定元素上有多个指令时,只创建一个作用域。并且可以在每个指令中设置一个priority,以定义应用指令的顺序;这些指令按优先级排序,然后调用它们的编译函数(在http://docs.angularjs.org/guide/directive中搜索单词priority)。
所以我希望我可以使用priority来确保我的指令首先运行并最终创建一个隔离作用域,当ng-repeat运行时,它会重新使用隔离作用域,而不是创建一个原型继承自父作用域的作用域。ng-repeat文档说明该指令在优先级别1000上运行。目前尚不清楚1是高优先级还是低优先级。当我在指令中使用1优先级时,它没有什么区别,所以我尝试了2000。但这让事情变得更糟:我的双向绑定变成了undefined,我的指令没有显示任何内容。
我创造了一把小提琴来展示我的问题。我已经在我的指令中对priority的设置进行了评论。我有一个名称对象列表和一个名为name-row的指令,它显示名称对象中的名字和姓氏字段。单击显示的名称时,我希望它在主作用域中设置一个selected变量。名称数组(selected变量)通过双向数据绑定传递给name-row指令。
我知道如何通过调用主范围中的函数来实现这一点。我也知道,如果selected在另一个物体内部,并且我绑定到外部物体,事情就会发生。但目前我对这些解决方案不感兴趣。
相反,我的问题是:
- 如何防止ng-repeat创建原型继承自父作用域的作用域,而让它使用我的指令的隔离作用域?
- 为什么我指令中的优先级别2000不起作用?
- 使用巴塔朗,是否可以知道使用的是哪种类型的范围?
- 通常,如果您的指令将与其他指令在同一元素上使用,则不希望使用隔离作用域。由于您正在创建自己的作用域属性,并且需要使用ng repeat,因此我建议在您的指令中使用scope: true。另请参见stackoverflow.com/questions/14914213/…(如果您还没有)另外,因为一个指令将在多个地方使用并不意味着我们应该自动使用一个隔离范围。
- 我读过你的许多答案(它们是非常好的,谢谢你写的),但我从来没有想到读过你的问题。我读了你链接到的内容。在我看来,隔离作用域指令不能与其他指令混合。我同意这种观点,即此类指令是组成部分,因此不需要与其他指令混合。到目前为止,我唯一的例外是ng-repeat。我认为能够将独立指令与ng-repeat混合是很有价值的。待续…
- 从上面继续…因此,如果一个元素应该只有一个具有作用域的指令,那么ng-repeat不应该有作用域。ng-repeat有一个范围对于典型的用例是有意义的,所以我不建议更改它。相反,正如我在亚历克斯·奥斯本的回答中所评论的,我认为我将创建一个基于ng-repeat的重复指令,它不会创建自己的范围。然后可以将其用于重复具有自己独立作用域的指令。待续…
- 重复指令的代码现在需要知道是使用ng-repeat还是使用自定义范围较小的repeat指令。我认为"调用者"知道这一点是可以的,但是"被调用者"(指令被重复)不知道它是否被重复。待续…
- 对这里的评论有点疯狂…:-)ngrepeat必须创建自己的作用域。为什么你觉得这里需要一个隔离镜?
- 此外,我认为组件(指令)能够在未来更改而不破坏使用它的代码是很重要的,特别是如果组件只是添加行为而不更改其"API"(即,它不需要额外的绑定来支持新行为)。新行为可能需要一个独立的作用域,在此之前不需要任何作用域,以便存储组件特定的状态。因此,我认为,根据组件的当前使用模式来决定"组件指令"使用哪种类型的范围并不理想。
- @乔希·戴维米勒,很抱歉发表了这么多评论。我第一次使用StackOverflow;在AngularJS的Google组中可能会更好。我做了一个尽可能小的小提琴来表示我不能把我的指令和ng-repeat混用。我的实际代码具有特定于指令的状态,我确实希望将其与父范围隔离开来。您认为我是否能够创建自己的ng-repeat变体,使用另一个指令的范围,或者ng-repeat中是否有一些基本的东西不能与另一个指令一起工作?我是一个JS新手,还没有看过角度代码。
- ngrepeat的工作原理是创建一组dom元素,向每个元素添加一个作用域,然后在每个作用域上创建一些包含迭代值和一些元数据的变量。没有单独的作用域就不能工作。如果你自己写,这将是容易出错的-它将要求已经有一个更高优先级的指令创建的作用域,然后你的新转发器将污染这个作用域。孤立无援!也就是说,您可以使用ngrepeat和一个隔离范围,所以我不确定问题是什么。
- 我的问题是:在带有隔离作用域的自定义指令中写入双向数据绑定在ng-repeat中失败。punker使用双向数据绑定,但它只读取绑定变量;它不写入绑定变量。我不需要写入指向当前迭代的变量(您的punker中的val),但是我在指令的隔离范围内绑定了其他变量,我需要能够从指令内修改这些变量。例如,在我的小提琴(在我的问题中链接到)中,我需要修改ioSelected,当我使用ng-repeat时,这不起作用。
- 哦,那么答案就更简单了。使用io-selected="$parent.selected"。
- 但也要记住,您的问题不在于ngrepeat,而在于原型继承在JavaScript中的工作方式。问题是你使用的是一个原语。模型值在AngularJS中应该总是有一个.。
- @Deepaknulu我认为您正在创建一个不需要的指令。似乎你正在尝试做的事情可以通过ng include和ng repeat的组合来实现。把每一个重复项目的控制器放进去,你就可以走了。
- @乔希·戴维米勒,我认为使用$parent是一种代码气味。如果我的指令嵌套在多个ng-repeat指令中怎么办?那么我的指示就是要知道兔子洞有多深,然后把一整串的埃多克斯(8)拴起来,对吗?ng-repeat引入了一个作用域,因为它需要使当前迭代对每个重复都可用。但是,如果我有一个重复变量,将当前迭代绑定到指令中的输入,该怎么办?那么repeat指令根本不需要引入范围。但我需要研究一下您所说的ng-repeat需求的元数据。
- @ganaraj my fiddle是一个最小的例子,它说明了当我使用ng-repeat时,我无法对双向绑定进行写入。我喜欢指令(和双向数据绑定)的概念,在我的应用程序(整个页面)中我有一个非常小的独立控制器(我希望我能去掉这个控制器,但我离题了)。因此,我正在寻找一种方法,使我的指令能够在任何具有双向数据绑定的场景中工作。
- 有人能帮我做这个吗?stackoverflow.com/questions/2200201/…
好吧,通过上面的很多评论,我发现了困惑。首先,澄清几个要点:
- ngrepeat不会影响您选择的隔离范围
- 传递给ngrepeat用于指令属性的参数确实使用了原型继承的作用域
- 指令不起作用的原因与隔离范围无关。
下面是相同代码的一个示例,但删除了指令:
1 2 3 4 5 6
| <li ng-repeat="name in names"
ng-class="{ active: $index == selected }"
ng-click="selected = $index">
{{$index}}: {{name.first}} {{name.last}}
</li> |
下面是一个JS小提琴,它表明它不起作用。您得到的结果与指令中的结果完全相同。
为什么不起作用?因为AngularJS中的作用域使用原型继承。父作用域上的值selected是一个原语。在JavaScript中,这意味着当子级设置相同的值时,它将被覆盖。在AngularJS范围中有一条黄金法则:模型值中应该始终有一个.。也就是说,他们不应该是原始人。有关更多信息,请参阅此答案。
这是一张最初范围的图片。
![enter image description here](//i1.wp.com/i.stack.imgur.com/5abQ5.png)
单击第一个项目后,作用域现在如下所示:
![enter image description here](//i1.wp.com/i.stack.imgur.com/mNipO.png)
注意,在ngrepeat作用域上创建了一个新的selected属性。未更改控制器范围003。
当我们单击第二项时,您可能会猜到发生了什么:
![enter image description here](//i1.wp.com/i.stack.imgur.com/1wyAf.png)
所以你的问题实际上根本不是由ngrepeat引起的——它是由违反安古拉吉的黄金法则引起的。修复它的方法是简单地使用一个对象属性:
1
| $scope.state = { selected: undefined }; |
1 2 3 4 5 6
| <li ng-repeat="name in names"
ng-class="{ active: $index == state.selected }"
ng-click="state.selected = $index">
{{$index}}: {{name.first}} {{name.last}}
</li> |
下面是第二个JSfiddle,它也显示了这一点。
以下是作用域最初的样子:
![enter image description here](//i1.wp.com/i.stack.imgur.com/xww7Z.png)
单击第一项后:
![enter image description here](//i1.wp.com/i.stack.imgur.com/POpOL.png)
在这里,控制器的作用域会根据需要受到影响。
另外,为了证明这仍然适用于隔离作用域的指令(因为,同样,这与您的问题无关),这里也有一个JShoddle,视图必须反映对象。您将注意到唯一必要的更改是使用对象而不是原语。
最初作用域:
![enter image description here](//i1.wp.com/i.stack.imgur.com/cjaw5.png)
单击第一项后的作用域:
![enter image description here](//i1.wp.com/i.stack.imgur.com/bSTOk.png)
最后总结:再一次,您的问题不在于隔离范围,而在于NGrepeat如何工作。你的问题是你违反了一条规则,这条规则会导致这个问题。AngularJS中的模型应该总是有一个.。
- 我对隔离作用域和双向数据绑定的指令进行的实验使我相信,只有具有原型继承的作用域才需要.黄金规则。对于隔离作用域,您感兴趣的变量在指令中的scope: {}定义中定义,因此这些变量从一开始就存在于隔离作用域中。此外,它们不会屏蔽父变量,因为隔离作用域不是从父作用域原型继承的。也就是说,没有要屏蔽的"父"范围。
- 但是,更新隔离作用域中的双向绑定变量(在scope: {}中用=定义)会导致外部作用域中的相应变量由于双向数据绑定而发生更改。我不记得在隔离作用域中用原语变量尝试过这种方法,但是我已经明确地看到了使用对象/引用变量的方法。注意,我所说的是为变量分配一个新的对象值,而不是修改对象内部的属性。也就是说,我没有遵守我的指令中的.黄金法则,它仍然有效。
- 我把我的小提琴分叉,用手工重复的方法替换了ng-repeat:jsfiddle.net/jtjk4/1(我为MainController中的每个索引创建了变量,因为我的指令使用inIndex : '='绑定索引)。你会发现这个很好用。我的指令在不使用.的情况下给ioSelected分配一个新的值,它工作正常(外部作用域看到更改并在HTML输出中显示所选名称)。这就是我要说的,是ng-repeat(特别是它的作用域)打破了我的双向数据绑定隔离作用域指令。我错过了什么?
- 我也有嵌套指令的情况,所有指令都有隔离作用域和双向数据绑定。绑定到外部指令的值需要传递到内部指令。需要一个包装对象(这样我就可以遵循.黄金法则)就变成了一个泄漏的抽象,迫使我重写指令。
- 对我来说,这意味着指令作为独立组件的承诺被打破了。请注意,我知道到目前为止提到的所有解决方案,我正在将函数传递给我的指令以克服这个问题。但我强烈地感到,需要一个更好的解决方案,让我们充分认识到指令和双向数据绑定的潜力。< /响应>
- 它实际上与隔离范围或指令无关。没有什么。ngrepeat必须创建自己的作用域才能正常工作;任何数量的其他组件也是如此。这意味着,即使你写了自己更脆弱的ngrepeat版本来强迫它在你的情况下为你工作,它也同样会在许多其他情况下(例如,超越)中断。你这样做只是为了打破规则!在AngularJS中,总是包含一个.。如果你不这样做,你会让它变得脆弱,最终会停止工作。我真正不明白的是为什么你一开始就抵制它。
- 还应该说:您的指令并没有创建子范围,但是它可以很容易地在需要子范围的上下文中使用。NGrepeat就是一个例子。超越也是如此。相信我-使用.。
- 关于安古拉吉斯谷歌集团的讨论,@joshdavidmiller终于能够澄清我的困惑!
- 乔希,我希望你不介意我在你的答案中加了一堆图片。有时一张照片价值1000条评论:)
- 一点也不!这些照片确实很有帮助。很快我就要开始复制你的任务单并自己创建。干得好,先生。
- 你们是怎么制作这些图表的?他们太棒了
- 你们从哪里得到这些很酷的图表?我也看到过其他用户发布这些内容。
- @阿萨德,我最近刚在Github上安装了这个工具,叫做peri$scope。
在不直接尝试避免回答问题的情况下,请看下面的小提琴:
http://jsfiddle.net/dvplm/
关键是,你不必试图去对抗和改变传统的角度行为,你可以构建你的指令来与ng-repeat合作,而不是试图去推翻它。
在模板中:
1 2 3 4
| <name-row
in-names-list="names"
io-selected="selected">
</name-row> |
在您的指令中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| template:
'
<ul>
' +
' <li ng-repeat="name in inNamesList" ng-class="activeClass($index)">' +
' ' +
' {{$index}} - {{name.first}} {{name.last}}' +
' ' +
'
</li>
' +
'
</ul>
' |
回答您的问题:
- ng-repeat将创建一个范围,您真的不应该试图改变这个范围。
- 指令中的优先级不仅仅是执行顺序-请参阅:angularjs:html编译器如何安排编译顺序?
- 在Batarang中,如果检查性能选项卡,您可以看到为每个作用域绑定的表达式,并检查它是否符合您的期望。
- 谢谢你这么快的答复。如果我所尝试的与实际情况不符,我会有点难过(尽管安古拉吉斯是一个很棒的框架)。指令是一个可重用的组件。写作时,作者不必担心它是否会与ng-repeat一起使用。也许当它第一次被写的时候,它永远不会和ng-repeat一起使用。在将来的某个时候,它可能会与ng-repeat一起使用,并且在那个时候,它应该可以在不重写的情况下工作。希望AngularJS的未来版本能够使这成为可能。
- 我想澄清一下我上面的评论。我认为可以编写一个不担心是否与ng-repeat一起使用的指令。但看起来我必须将一个函数传递给该指令,这样它就可以修改父范围中的变量,而不是改变指令自身范围中的双向绑定。双向绑定和可重用的指令是我最喜欢的两个关于AngularJS和EDOCX1的东西,事实证明这是一个好方法。也许我可以写一个不创建自己范围的ng-repeat等价物。