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

时间:2013-03-25 20:02:44

标签: angularjs angularjs-directive angularjs-scope angularjs-ng-repeat

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

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

所以我理解我的代码因ng-repeat指令创建的范围而失败。我自己的指令创建了一个isolate-scope,并对父作用域中的对象进行双向数据绑定。我的指令将为此绑定变量分配一个新的对象值,当我的指令在没有ng-repeat的情况下使用时(父变量被正确更新),这种方法非常有效。但是,对于ng-repeat,赋值会在ng-repeat范围内创建一个新变量,而父变量则看不到更改。所有这些都是基于我所读到的预期。

我还读过,当给定元素上有多个指令时,只创建一个范围。并且可以在每个指令中设置priority以定义指令的应用顺序;指令按优先级排序,然后调用它们的编译函数(在http://docs.angularjs.org/guide/directive搜索单词优先级。)。

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

我创建了a fiddle to show my issue。我在我的指令中注释了priority设置。我有一个名称对象列表和一个名为name-row的指令,它显示了名称对象中的名字和姓氏字段。单击显示的名称时,我希望它在主范围中设置selected变量。使用双向数据绑定将名称数组selected变量传递给name-row指令。

我知道如何通过调用主范围中的函数来使其工作。我也知道,如果selected在另一个对象中,并且我绑定到外部对象,事情就会起作用。但我现在对这些解决方案不感兴趣。

相反,我的问题是:

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

2 个答案:

答案 0 :(得分:203)

好的,通过上面的很多评论,我发现了混乱。首先,澄清几点:

  • ngRepeat不会影响您选择的隔离范围
  • 传入ngRepeat的参数用于指令的属性 do 使用原型继承的范围
  • 您的指令不起作用的原因与隔离范围
  • 无关

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

<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范围中有一条黄金法则:模型值始终中包含.。也就是说,它们永远不应该是原始的。有关详细信息,请参阅this SO answer


以下是范围最初的样子。

enter image description here

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

enter image description here

请注意,在ngRepeat范围内创建了新的selected属性。控制器范围003未被更改。

你可能猜到当我们点击第二项时会发生什么:

enter image description here


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

这是second JSFiddle,显示这也有效。

这是范围最初的样子:

enter image description here

点击第一项后:

enter image description here

此处,控制器范围正在受到影响。

另外,为了证明这仍然适用于具有隔离范围的指令(因为,这与您的问题无关),这里也是JSFiddle,视图必须反映物体。您会注意到唯一必要的更改是使用对象而不是原语

最初的范围:

enter image description here

点击第一项后的范围:

enter image description here

总结:再一次,您的问题不在于隔离范围,而在于ngRepeat的工作原理。你的问题是,你正在打破一个已知导致这个问题的规则。 AngularJS中的模型应始终具有.

答案 1 :(得分: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>'

回答你的问题: