passando 2 $ valores de índice dentro de ng-repeat aninhada


322

Então, eu tenho um ng-repeat aninhado dentro de outro ng-repeat para criar um menu de navegação. Em cada um <li>no loop ng-repeat interno, defino um ng-click que chama o controlador relevante para esse item de menu, passando o índice $ para que o aplicativo saiba qual deles precisamos. No entanto, eu também preciso passar o índice $ do ng-repeat externo para que o aplicativo saiba em qual seção estamos e qual tutorial.

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

aqui está um Plunker http://plnkr.co/edit/bJUhI9oGEQIql9tahIJN?p=preview


Por que você deseja passar o $ index? basta passar a referência do objeto assim ng-click="loadFromMenu(section)". Passar $ index significa que você fará um loop para encontrar o objeto desnecessário.
— Moshii 25/11

Respostas:


468

Cada ng-repeat cria um escopo filho com os dados passados ​​e também adiciona uma $indexvariável adicional nesse escopo.

Portanto, o que você precisa fazer é alcançar o escopo pai e usá-lo $index.

Consulte 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>

incrível, é assim que acesso o índice $ externo, mas eu preciso passar os dois valores do índice $. Eu tentei colocá-los em uma matriz e funcionou :)
— Tules

16
Você não precisa usar uma matriz: ng-click="loadFromMenu($parent.$index, $index)"
— Mark Rajcok

3
você nem precisa passar os índices - no loadFromMenu, ele deve ter acesso a esse objeto, o que lhe dará acesso a: this. $ index e this. $ parent. $ index
— Oddman

3
@ Oddman, embora isso seja possível, não acho que seja uma boa ideia, pois você liga o loadFromMenu para executar no contexto de um objeto que tem um escopo com um índice $ e um escopo pai com um índice $. suponha que você, por exemplo, crie grupos dentro do menu que geram outro escopo entre os dois significativos - você precisará alterar o loadFromMenu em vez de alterar a chamada para ele. E se em alguns menus você precisar ligar loadFromMenu($parent.$parent.$index,$index)e em alguns você precisar loadFromMenu($parent.$index,$index), o uso this....simplesmente não funcionaria.
— epeleg 5/03/2014

7
Você não deve usar o índice $ parent. $, Pois não é realmente seguro. Se você adicionar um ng-se dentro do loop, você começa a $ index confuso, cheque: plnkr.co/52oIhLfeXXI9ZAynTuAJ
— gonzalon

201

Solução muito mais elegante do que $parent.$indexestá usando ng-init:

<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
Este é o método recomendado para alcançar esse efeito. De fato, a equipe angular expressou algumas vezes que esse é um dos poucos usos recomendados da diretiva ng-init (consulte: link ). Costumo achar que o uso de $ parent (de acordo com a resposta atualmente aceita) é um pouco de cheiro de código, pois geralmente há uma maneira melhor de alcançar a comunicação de escopo $ a $ scope (Serviços ou Eventos de Transmissão / Emissão) e atribuí-lo a um nomeada variável, fica mais claro o que o valor representa.
— David Beech

2
OMI esta resposta é muito melhor do que o aceito. Usar $ parent é muito, muito desaconselhável.
— precisa saber é o seguinte

43
Isso para de funcionar quando você remove itens da lista porque o valor ng-init não é reinicializado. Só é útil se você não pretende remover itens da lista.
— Jesse Greathouse

6
Parece que a sintaxe angular evoluiu. stackoverflow.com/a/31477492/2516560 agora você pode usar "ng-repeat = (chave, valor) nos dados"
— Okazari 17/07/15

2
Na época em que escrevi essa resposta, essa era a maneira de fazê-lo no Angular 1.X. Esse uso de ng-initfoi demonstrado em ng-initdocumentos sem menção única nos ng-repeatdocumentos, então eu escrevi aqui + alterei os documentos no Github. A ng-repeatsintaxe atual tem uma maneira melhor de conseguir isso, o que é demonstrado pelo @Okazari. Está livre de algumas imperfeições mencionadas por você nos comentários.
— Vucalur # 9/16

115

Que tal usar esta sintaxe (veja este plunker ). Acabei de descobrir isso e é incrível.

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

Exemplo:

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

Com esta sintaxe, você pode dar seu próprio nome $indexe diferenciar os dois índices.


8
Eu acho que isso é muito mais limpo do que usar pai quando você tem vários loops aninhados
— Yasitha Waduge

Na verdade, isso me causou alguns problemas reais ao usar a angular-ui-tree para arrastar / soltar nós. O índice não parece ser redefinido e coisas estranhas acontecem como resultado.
— Mike Taverne

@MikeTaverne Você poderia tentar reproduzir em um plunker? Eu adoraria ver o comportamento.
— Okazari

4
Essa é a maneira correta de fazer isso e evitar possíveis problemas. O ng-init funciona para a renderização inicial, mas se o objeto for atualizado na página, ele será interrompido.
— Eric Martin

Boa! mas corrija adicionando "rastrear por", como no exemplo de plunker.
— Danilo

32

Só para ajudar alguém que chega aqui ... Você não deve usar $ parent. $ Index, pois não é realmente seguro. Se você adicionar um ng-if dentro do loop, você terá o $ index bagunçado!

Caminho certo

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

Confira: plnkr.co/52oIhLfeXXI9ZAynTuAJ


Observe que o "rastrear por" não é necessário aqui - isso é uma coisa separada usada pelo ng-repeat para entender itens exclusivos e observar as alterações na coleção (consulte a seção "Rastreamento e duplicatas" dos documentos: docs.angularjs.org / api / ng / diretiva / ngRepeat ). O ponto importante aqui é o "ng-init" - essa é a maneira correta com a qual os documentos angulares concordam.
— Rebecca

@gonzalon Como passar esses índices como parâmetro em ng click
— Nagaraj S

Estou fazendo removeList ($ index) ou removeList (rowIndex) Não está gravando o índice corretamente na função Recebo o valor do índice como indefinido. Estou usando o angular 1.5.6
— user269867

Este é o caminho certo, você nunca deve acessar $ parent
— Jesu Castillo

3

Ao lidar com objetos, você deseja ignorar os IDs simples, tanto quanto for conveniente.

Se você alterar a linha do clique para isso, acho que você estará bem no seu caminho:

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

Além disso, acho que você pode precisar mudar

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

para algo como

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

Veja http://docs.angularjs.org/misc/faq e procure por ng-class.


1

Apenas use ng-repeat="(sectionIndex, section) in sections"

e isso será útil no próximo nível ng-repeat down.

<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>
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.