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

Directive isolate scope with ng-repeat scope in AngularJS

我有一个带有隔离作用域的指令(这样我可以在其他地方重用该指令),当我将该指令与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不起作用?
  • 使用巴塔朗,是否可以知道使用的是哪种类型的范围?


好吧,通过上面的很多评论,我发现了困惑。首先,澄清几个要点:

  • 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

单击第一个项目后,作用域现在如下所示:

enter image description here

注意,在ngrepeat作用域上创建了一个新的selected属性。未更改控制器范围003。

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

enter image description here

所以你的问题实际上根本不是由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

单击第一项后:

enter image description here

在这里,控制器的作用域会根据需要受到影响。

另外,为了证明这仍然适用于隔离作用域的指令(因为,同样,这与您的问题无关),这里也有一个JShoddle,视图必须反映对象。您将注意到唯一必要的更改是使用对象而不是原语。

最初作用域:

enter image description here

单击第一项后的作用域:

enter image description here

最后总结:再一次,您的问题不在于隔离范围,而在于NGrepeat如何工作。你的问题是你违反了一条规则,这条规则会导致这个问题。AngularJS中的模型应该总是有一个.


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

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中,如果检查性能选项卡,您可以看到为每个作用域绑定的表达式,并检查它是否符合您的期望。