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

Tules
fonte
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>
Alex Osborn
fonte
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

vucalur
fonte
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.

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

gonzalon
fonte
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.

Kwerle
fonte
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>
RobKohr
fonte