335

So I have an ng-repeat nested within another ng-repeat in order to build a nav menu. On each <li> on the inner ng-repeat loop I set an ng-click which calls the relevant controller for that menu item by passing in the $index to let the app know which one we need. However I need to also pass in the $index from the outer ng-repeat so the app knows which section we are in as well as which 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> 

here's a Plunker http://plnkr.co/edit/bJUhI9oGEQIql9tahIJN?p=preview

1
  • Why do you want to pass $index? just pass the object reference like this ng-click="loadFromMenu(section)". Passing $index means you will do a loop to find the object which is unnecessary. Commented Nov 25, 2017 at 18:38

6 Answers 6

487

Each ng-repeat creates a child scope with the passed data, and also adds an additional $index variable in that scope.

So what you need to do is reach up to the parent scope, and use that $index.

See 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> 
Sign up to request clarification or add additional context in comments.

11 Comments

awesome, so that's how I access the outer $index, but I needed to pass in BOTH $index values. I tried putting them in an array and it worked :)
You don't have to use an array: ng-click="loadFromMenu($parent.$index, $index)"
you don't even have to pass the indexes in - in the loadFromMenu, it should have access to a this object, which will give you access to: this.$index and this.$parent.$index
@Oddman even though this is possible I don't think its a good idea as you then hard wire your loadFromMenu to run at a context of an object that has a scope with an $index and a parent scope with an $index. suppose you then for example create groups within the menu that generate another scope in between the two significant ones - you will have to change loadFromMenu instead of changing the call to it. And if in some menues you need to call loadFromMenu($parent.$parent.$index,$index) and in some you need just loadFromMenu($parent.$index,$index) then using this.... would just not work.
You should not use $parent.$index as it's not really safe. If you add an ng-if inside the loop, you get the $index messed, check: plnkr.co/52oIhLfeXXI9ZAynTuAJ
|
209

Way more elegant solution than $parent.$index is using 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

9 Comments

This is the recommended method of achieving this effect. In fact the angular team have expressed a few times that this is one of the few recommended uses of the ng-init directive (see: link). I often find use of $parent (as per the currently accepted answer) is a bit of a code smell as there's usually a better way of achieving $scope to $scope communication (Services or Broadcast/Emit Events) and by assigning it to a named variable it becomes clearer what the value represents.
IMO this answer is way better than the accepted. Using $parent is really, really unadvised.
This stops working when you remove items from the list because the ng-init value doesn't re-initialize. It is only useful if you don't intend to remove items from the list.
Looks like angular syntax evolved. stackoverflow.com/a/31477492/2516560 you can now use "ng-repeat = (key,value) in data"
Back in the days then I wrote this answer, this was the way to do it for Angular 1.X. This use of ng-init was demonstrated in ng-init docs with no single mention in ng-repeat docs, so I wrote here + amended the docs on Github. Current ng-repeat syntax has a better way to achieve this, which is demonstrated by @Okazari. It is free of some imperfections mentioned by you in the comments.
|
124
+50

What about using this syntax (take a look in this plunker). I just discovered this and it's pretty awesome.

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

Example:

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

With this syntax you can give your own name to $index and differentiate the two indexes.

6 Comments

I think this is much cleaner than using parent when you have multiple nested loops
Actually this caused me some real problems when using the angular-ui-tree to drag/drop nodes. The index doesn't seem to get reset and weird things happen as a result.
@MikeTaverne Could you try to reproduce in a plunker ? I'd love to see the behavior.
This is the correct way to accomplish this and avoid any potential issues. ng-init works for the initial render, but if the object is updated on the page, it breaks.
good! but please fix with adding "track by" like the plunker example.
|
34

Just to help someone who get here... You should not use $parent.$index as it's not really safe. If you add an ng-if inside the loop, you get the $index messed!

Right way

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

Check: plnkr.co/52oIhLfeXXI9ZAynTuAJ

4 Comments

Note that the "track by" isn't needed here - that's a separate thing used for ng-repeat to understand unique items and watch for changes in the collection (see "Tracking and Duplicates" section of the docs: docs.angularjs.org/api/ng/directive/ngRepeat). The important point here is the "ng-init" - that is the correct way that the angular docs agree with.
@gonzalon How to pass these index as parameter in ng click
I am doing removeList($index) or removeList(rowIndex) It is not recording the index correctly in function I receive index value as undefined . I am using angular 1.5.6
This is the right way, you should never access $parent
4

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

and that will be useable on the next level 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> 

Comments

3

When you are dealing with objects, you want to ignore simple id's as much as convenient.

If you change the click line to this, I think you will be well on your way:

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

Also, I think you may need to change

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

to something like

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

See http://docs.angularjs.org/misc/faq and look for ng-class.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.