首页 文章

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

提问于
浏览
94

我有一个带有isolate-scope的指令(这样我可以在其他地方重用该指令),当我将这个指令与 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 回答

  • 200

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

    • 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中的范围使用原型继承 . 父范围上的值 selectedprimitive . 在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

    点击第一项后的范围:

    总结:再一次,您的问题与ngRepeat的工作方式不同 . 你的问题是,你正在打破一个已知导致这个问题的规则 . AngularJS中的模型应始终具有 . .

  • 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>'
    

    回答你的问题:

相关问题