AngularJS中具有ng-repeat范围的指令隔离范围


95

我有一个带有隔离范围的指令(以便我可以在其他地方重用该指令),当我将此指令与一起使用时ng-repeat,它将无法正常工作。

我已经阅读了有关此主题的所有文档和Stack Overflow的答案,并了解了这些问题。我相信我避免了所有常见的陷阱。

因此,我知道我的代码由于ng-repeat指令创建的作用域而失败。我自己的指令创建了一个隔离范围,并与父作用域中的对象进行了双向数据绑定。我的指令将为该绑定变量分配一个新的对象值,并且在不使用我的指令的情况下ng-repeat(父变量已正确更新),这可以完美地工作。但是,使用时ng-repeat,赋值将在ng-repeat作用域中创建一个新变量,并且父变量看不到更改。所有这些都是根据我所读的内容预期的。

我还读到,当给定元素上有多个指令时,只会创建一个作用域。并且priority可以在每个指令中设置a,以定义指令的应用顺序;指令按优先级排序,然后调用其编译功能(在http://docs.angularjs.org/guide/directive中搜索优先级一词)。

因此,我希望可以使用优先级来确保我的指令先运行并最终创建一个隔离范围,并且在ng-repeat运行时,它会重新使用隔离范围,而不是创建一个原型上从父范围继承的范围。该ng-repeat文件指出,在优先级别的指令运行1000。不清楚1是较高优先级还是较低优先级。当我1在指令中使用优先级时,它没有什么不同,因此我尝试了2000。但这使情况变得更糟:我的双向绑定变为undefined,我的指令不显示任何内容。

我制造了一个小提琴来展示我的问题。我已经priority在指令中注释掉了设置。我有一个名称对象列表和一个名为的指令,该指令name-row显示名称对象中的名字和姓氏字段。单击显示的名称时,我希望它selected在主作用域中设置一个变量。名称数组,selected变量name-row使用双向数据绑定传递给指令。

我知道如何通过在主作用域中调用函数来使其工作。我也知道,如果selected在另一个对象内部,并且绑定到外部对象,则一切正常。但是我目前对这些解决方案不感兴趣。

相反,我的问题是:

  • 如何防止ng-repeat创建从父作用域继承的原型作用域,而使用我的指令的isolate-scope?
  • 为什么2000我的指令中的优先级不起作用?
  • 使用Batarang,是否可以知道正在使用哪种类型的示波器?

1
通常,如果您的指令将与其他指令一起用于同一元素,则您不想使用隔离作用域。由于您正在创建自己的作用域属性,并且需要使用ng-repeat,因此建议scope: true您为指令使用。另请参阅(如果尚未使用的话)stackoverflow.com/questions/14914213/… 另外,仅因为指令将在多个地方使用并不意味着我们应该自动使用隔离范围。
Mark Rajcok

我已经阅读了您的许多答案(它们非常出色,感谢您写的答案),但是我从未想到要阅读您的问题:-)。我读了你链接的内容。在我看来,isolate-scope指令不能与其他指令混合使用。我同意这种指令是组件的观点,因此不需要将它们与其他指令混合使用。(到目前为止)对我来说是一个例外ng-repeat。我认为能够将独立指令与混合使用非常有价值ng-repeat。待续...
Deepak Nulu

从上接下...因此,如果应该只有一个指令具有一个元素范围,那么就ng-repeat不应具有范围。ng-repeat具有作用域对于典型用例确实有意义,因此我不建议更改它。相反,就像我在Alex Osborn的答案中评论的那样,我认为我将基于该重复指令ng-repeat创建自身的范围。然后可以将其用于重复指令,这些指令具有自己的隔离范围。待续...
Deepak Nulu

现在,重复指令的代码需要知道是使用ng-repeat还是自定义的无作用域重复指令。我认为“调用方”知道这一点是可以的,但是“被调用方”(被重复的指令)知道是否被重复是不可行的。待续...
迪帕克·努卢

对这里的评论有点疯狂... :-) ngRepeat 必须创建自己的作用域。为什么您觉得这里需要隔离示波器?
Josh David Miller

Answers:


203

好的,通过上面的很多评论,我发现了这种困惑。首先,有两点需要澄清:

  • ngRepeat不会影响您选择的隔离范围
  • 传递给ngRepeat以便在指令的属性上使用的参数确实使用了原型继承的范围
  • 您的指令不起作用的原因与隔离范围无关

这是相同代码的示例,但删除了伪指令:

<li ng-repeat="name in names"
    ng-class="{ active: $index == selected }"
    ng-click="selected = $index">
    {{$index}}: {{name.first}} {{name.last}}
</li>

这是一个JSFiddle,表明它不起作用。您将获得与指令中完全相同的结果。

为什么不起作用?因为AngularJS中的范围使用原型继承。selected父范围的值是原始值。在JavaScript中,这意味着当子级设置相同的值时,它将被覆盖。AngularJS范围中有一个黄金法则:模型值应始终包含一个.。也就是说,它们永远都不应该是原始的。有关更多信息,请参见此SO答案


这是示波器最初的样子。

在此处输入图片说明

单击第一项后,范围现在如下所示:

在此处输入图片说明

注意,selected在ngRepeat范围上创建了一个新属性。控制器范围003未被更改。

您可能猜出当我们单击第二项时会发生什么:

在此处输入图片说明


因此,您的问题实际上根本不是由ngRepeat引起的-它是由AngularJS中的黄金法则引起的。修复它的方法是简单地使用对象属性:

$scope.state = { selected: undefined };
<li ng-repeat="name in names"
    ng-class="{ active: $index == state.selected }"
    ng-click="state.selected = $index">
    {{$index}}: {{name.first}} {{name.last}}
</li>

这是第二个JSFiddle,它也显示了这一功能。

范围最初是这样的:

在此处输入图片说明

单击第一项后:

在此处输入图片说明

在这里,控制器作用域会受到影响。

同样,为了证明这仍然可以在具有隔离范围的指令中使用(因为这又与您的问题无关),这也是一个JSFiddle,因此视图必须反映该对象。您会注意到,唯一必要的更改是使用对象而不是原始对象

最初的范围:

在此处输入图片说明

单击第一项后的范围:

在此处输入图片说明

总结一下:再一次,您的问题不在于孤立范围,也不在于ngRepeat的工作方式。您的问题是您违反了导致此问题的已知规则。AngularJS中的模型应始终带有.


我对带有隔离范围和2路数据绑定的指令的实验使我相信,.只有具有原型继承的作用域才需要黄金法则。使用isolate-scope,scope: {}在指令的定义中定义了您感兴趣的变量,因此这些变量将从一开始就存在于isolate-scope中。而且,它们不会掩盖父变量,因为isolate-scope并不原型上继承自父作用域。即没有“父”范围要掩盖。
Deepak Nulu

1
还应该说:您的指令没有创建子范围,但是可以很容易地在需要子范围的上下文中使用。ngRepeat是一种情况。包容性也是如此。相信我-使用。.
乔什·大卫·米勒

1
在AngularJS Google小组进行了讨论,@ JoshDavidMiller终于能够消除我的困惑!
Deepak Nulu

2
你们从哪里得到所有这些很棒的图?我也看到其他用户也发布了这些内容。
Asad Saeeduddin 2014年

3
@Asad,我最近才将该工具放在GitHub上,称为Peri $ scope
Mark Rajcok

6

在不直接尝试避免回答您的问题的情况下,请看以下小提琴:

http://jsfiddle.net/dVPLM/

关键是,与其尝试对抗和改变Angular的常规行为,ng-repeat不如尝试覆盖它,而可以将指令构造为可以使用。

在您的模板中:

    <name-row 
        in-names-list="names"
        io-selected="selected">
    </name-row>

在您的指令中:

    template:
'        <ul>' +      
'            <li ng-repeat="name in inNamesList" ng-class="activeClass($index)" >' +
'                <a ng-click="setSelected($index)">' +
'                    {{$index}} - {{name.first}} {{name.last}}' +
'                </a>' +
'            </li>' +
'        </ul>'

针对您的问题:

  • ng-repeat 将创建一个范围,您真的不应该尝试更改此范围。
  • 指令中的优先级不仅是执行顺序-参见:AngularJS:HTML编译器如何安排编译顺序?
  • 在Batarang中,如果选中“性能”选项卡,则可以看到绑定到每个范围的表达式,并检查其是否符合您的期望。

感谢您的快速回复。如果我尝试的事情与事实不符,我会感到有点难过(AngularJS仍然是一个很棒的框架)。指令应成为可重用的组件。在编写时,作者不必担心它是否将与一起使用ng-repeat。也许当它第一次被编写时,它从未与一起使用ng-repeat。而且在将来的某个时间,它可能会与一起使用ng-repeat,并且在那个时候,它应该可以正常工作而无需重写。希望将来的AngularJS版本能够实现这一目标。
Deepak Nulu

我想在上面澄清我的评论。我认为有可能编写不担心是否正在使用该指令的指令ng-repeat。但是似乎我必须将一个函数传递给指令,以便它可以修改父作用域中的变量,而不是能够在指令自己的作用域中更改双向绑定。双向绑定和可重用指令是我最喜欢AngularJS的两件事,事实ng-repeat证明这是一种美中不足。也许我可以写一个ng-repeat不创建自己范围的等效项。
Deepak Nulu
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.