在嵌套的ng-repeat中传递2个$index值

335
所以我有一个ng-repeat嵌套在另一个ng-repeat中,以构建导航菜单。在内部ng-repeat循环的每个
  • 上,我设置了一个ng-click,它通过传递$index来调用该菜单项的相关控制器,以让应用程序知道我们需要哪一个。然而,我还需要传递外部ng-repeat的$index,以便应用程序知道我们在哪个部分以及哪个教程。
    <ul ng-repeat="section in sections">
        <li  class="section_title {{section.active}}" >
            {{section.name}}
        </li>
        <ul>
            <li class="tutorial_title {{tutorial.active}}" ng-click="loadFromMenu($index)" ng-repeat="tutorial in section.tutorials">
                {{tutorial.name}}
            </li>
        </ul>
    </ul>
    

    这是一个 Plunker http://plnkr.co/edit/bJUhI9oGEQIql9tahIJN?p=preview


  • 为什么要传递$index?只需像这样传递对象引用ng-click="loadFromMenu(section)"。传递$index意味着您将执行一个循环来查找不必要的对象。 - Moshi
    6个回答

    487
    每个ng-repeat都会使用传递的数据创建一个子作用域,并在该作用域中添加额外的$index变量。
    因此,您需要访问父作用域并使用那个$index变量。
    请参见 http://plnkr.co/edit/FvVhirpoOF8TYnIVygE6?p=preview
    <li class="tutorial_title {{tutorial.active}}" ng-click="loadFromMenu($parent.$index)" ng-repeat="tutorial in section.tutorials">
        {{tutorial.name}}
    </li>
    

    18
    你不必使用数组:ng-click="loadFromMenu($parent.$index, $index)" - Mark Rajcok
    3
    在loadFromMenu中,甚至不需要传递索引,它应该可以访问this对象,从而让你可以访问this.$index和this.$parent.$index。请注意保留原文意思,同时使翻译更通俗易懂。 - Oddman
    3
    尽管这是可能的,但我认为这并不是一个好主意,因为这样你就把loadFromMenu硬编码为在具有$index范围和父范围带有$index的对象上下文中运行。假设你在菜单中创建了生成另一个重要范围之间的另一个范围的组 - 你将不得不更改loadFromMenu而不是更改对它的调用。如果在某些菜单中需要调用'loadFromMenu($parent.$parent.$index,$index)',而在一些菜单中只需要'loadFromMenu($parent.$index,$index)',那么使用'this...'就行不通。 - epeleg
    同意。通常我做这种事情时会构建特定于资源的指令,所以这不是问题。 - Oddman
    7
    不应该使用$parent.$index,因为它并不是非常安全的。如果在循环内添加ng-if,$index将会混乱,请参考: http://plnkr.co/52oIhLfeXXI9ZAynTuAJ - Gonz
    显示剩余6条评论

    208

    使用 ng-init 要比 $parent.$index 更加优雅的解决方案:

    <ul ng-repeat="section in sections" ng-init="sectionIndex = $index">
        <li  class="section_title {{section.active}}" >
            {{section.name}}
        </li>
        <ul>
            <li class="tutorial_title {{tutorial.active}}" ng-click="loadFromMenu(sectionIndex)" ng-repeat="tutorial in section.tutorials">
                {{tutorial.name}}
            </li>
        </ul>
    </ul>
    

    Plunker: http://plnkr.co/edit/knwGEnOsAWLhLieKVItS?p=info


    2
    这是实现此效果的推荐方法。事实上,Angular团队已经多次表达过这是ng-init指令的少数推荐用法之一(请参见:链接)。我经常发现使用$parent(根据当前接受的答案)有点像代码异味,因为通常有更好的方法来实现$scope到$scope的通信(服务或广播/发射事件),并通过将其分配给命名变量,可以更清楚地表示该值代表什么。 - David Beech
    2
    在我看来,这个答案比被接受的答案好得多。真的,真的不建议使用$parent。 - olivarra1
    46
    当你从列表中删除项目时,这种方法就无法发挥作用,因为ng-init的值不会重新初始化。只有在不打算删除列表中的项目时才有用。 - Jesse Greathouse
    6
    似乎Angular语法有所发展。现在您可以使用“ng-repeat =(key,value)in data”来重复循环数据。 - Okazari
    2
    回到我写这个答案的那个年代,这是 Angular 1.X 的标准方式。ng-init 的使用在 ng-init 文档中有示范,但在 ng-repeat 文档中没有提到,因此我在这里写了一篇文章,并在 Github 上修改了文档。目前的 ng-repeat 语法有更好的方法来实现这个,由 @Okazari 示范。它摆脱了你在评论中提到的一些缺陷。 - vucalur
    显示剩余4条评论

    124

    使用这种语法怎么样(在这个 plunker 中看一下)。我刚刚发现了这个,它非常棒。

    ng-repeat="(key,value) in data"
    

    例子:

    <div ng-repeat="(indexX,object) in data">
        <div ng-repeat="(indexY,value) in object">
           {{indexX}} - {{indexY}} - {{value}}
        </div>
    </div>
    

    使用这个语法,您可以为$index指定自己的名称并区分这两个索引。


    8
    在存在多个嵌套循环时,我认为这种写法比使用“父级(parent)”更加简洁。 - Yasitha Waduge
    实际上,当我使用angular-ui-tree拖放节点时,这给我带来了一些真正的问题。索引似乎没有被重置,结果会发生奇怪的事情。 - Mike Taverne
    @MikeTaverne,你能在Plunker上尝试重现一下吗?我很想看看这个行为。 - Okazari
    4
    这是完成此操作并避免任何潜在问题的正确方法。ng-init适用于初始呈现,但如果页面上的对象更新,它会出现问题。 - Eric Martin
    很好!但请添加“track by”以确保像Plunker示例一样正常运行。 - Danilo
    @Danilo 嘿,Danilo,我不会在 Plunker 中添加我的“track by”示例,因为使用索引作为 track by 的标识符是一种不好的做法。您应该始终使用特定于对象的标识符(例如 id)。由于我的示例中没有 id,因此我在 Plunker 中的“track by”只是一个解决方法。 - Okazari

    34

    只是为了帮助到某些人到达这里... 你不应该使用 $parent.$index,因为它并不是真正安全的。如果在循环内部添加 ng-if,则会导致 $index 混乱!

    正确的方法

      <table>
        <tr ng-repeat="row in rows track by $index" ng-init="rowIndex = $index">
            <td ng-repeat="column in columns track by $index" ng-init="columnIndex = $index">
    
              <b ng-if="rowIndex == columnIndex">[{{rowIndex}} - {{columnIndex}}]</b>
              <small ng-if="rowIndex != columnIndex">[{{rowIndex}} - {{columnIndex}}]</small>
    
            </td>
        </tr>
      </table>
    

    检查:plnkr.co/52oIhLfeXXI9ZAynTuAJ


    1
    请注意,这里不需要使用“track by” - 这是用于ng-repeat理解唯一项并监视集合中更改的单独内容(请参见文档中的“跟踪和重复项”部分:https://docs.angularjs.org/api/ng/directive/ngRepeat)。这里的重点是“ng-init” - 这是与Angular文档一致的正确方式。 - Rebecca
    @gonzalon 如何将这些索引作为参数传递给ng-click函数? - Nagaraj S
    我正在执行removeList($index)或removeList(rowIndex)。在函数中,它没有正确记录索引,我收到的索引值为未定义。我正在使用angular 1.5.6。 - user269867
    这是正确的方式,你永远不应该访问$parent。 - Jesú Castillo

    4

    只需使用ng-repeat="(sectionIndex, section) in sections"

    就可以在下一级的ng-repeat中使用它。

    <ul ng-repeat="(sectionIndex, section) in sections">
        <li  class="section_title {{section.active}}" >
            {{section.name}}
        </li>
        <ul>
            <li ng-repeat="tutorial in section.tutorials">
                {{tutorial.name}}, Your section index is {{sectionIndex}}
            </li>
        </ul>
    </ul>
    

    3

    当你处理对象时,尽可能忽略简单的 ID。

    如果你将点击行改成这样,我认为你就走上了正确的道路:

    <li class="tutorial_title {{tutorial.active}}" ng-click="loadFromMenu(tutorial)" ng-repeat="tutorial in section.tutorials">
    

    此外,我认为您可能需要进行更改。
    class="tutorial_title {{tutorial.active}}"
    

    转换为类似于

    ng-class="tutorial_title {{tutorial.active}}"
    

    请参考http://docs.angularjs.org/misc/faq,查找ng-class相关信息。


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接