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创建原型从父范围继承的范围,而是让它使用我的指令的隔离范围?
  • 为什么2000我的指令中的优先级不起作用?
  • 使用 Batarang,是否有可能知道正在使用什么类型的范围?
4

2 回答 2

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,视图必须反映对象。您会注意到,唯一必要的更改是使用object而不是原语

最初的范围:

在此处输入图像描述

单击第一项后的范围:

在此处输入图像描述

总结:再一次,您的问题不在于隔离范围,也不在于 ngRepeat 的工作方式。您的问题是您违反了已知会导致此问题的规则。AngularJS 中的模型应该总是有一个..

于 2013-03-26T19:06:14.937 回答
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 中,如果您检查性能选项卡,您可以看到为每个范围绑定的表达式,并检查这是否符合您的期望。
于 2013-03-25T20:34:26.170 回答