passing 2 $index values within nested ng-repeat - angularjs

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

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>

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

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.

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

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>

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.

Related

Number of watchers adding up with ng-repeat

The below code renders tabs, sections under each tab and the content of the section. When navigating from one section to another, I notice the number of watchers are increasing (or) adding up each time.
Can any one tell what could be the reason for this. Appreciate any pointers
<ul class="nav nav-tabs">
<li ng-repeat="tab in tabList track by tab.tabId" id="{{tab.attribute}}"
ng-class="{active:selectedTab.attribute === '{{tab.attribute}}'}">
<a showtab="" href="#{{tab.attribute}}" ng-click="setSelectedTab(tab)">{{tab.displayLabel}}</a>
</li>
</ul>
<div class="tab-pane">
<div class="" ng-if="selectedTab.sectionList.length > 0">
<ul class="nav nav-pills pills-section">
<li ng-repeat="section in selectedTab.sectionList track by section.sectionId" id="{{section.attribute}}" ng-class="{active:selectedSection.attribute === '{{section.attribute}}'}">
<a showtab="" href="#{{section.attribute}}" ng-click="setSelectedSection(section)">{{section.displayLabel}}</a>
</li>
</ul>
<!-- Content of the section rendered with ng-repeat -->
<div ng-include="sectionContent"/>
</div>
</div>
The section content has the below mark up
<table>
<tbody>
<tr ng-repeat="field in selectedSection.fields track by field.fieldId">
</tr>
</tbody>
</table>
I have had similar problems before, nesting ng-repeats is never a good idea, but we are forced from time to time to do it. This is your solution to have performance benefits.
http://kamilkp.github.io/angular-vs-repeat/

Skip first item in ng-repeat

I want to skip the first item in ng-repeat
..first item here
<ul class="highlight-topright">
<li ng-repeat="item in hpHeadlines | filter:$index>0">
...
</li>
</ul>
it doesn't work, anything wrong here ?
I understand that i can use ng-if, ng-show to hide things but I just cannot get why the filter doesn't work in 1.3.x in this case.
Thanks.
<ul class="highlight-topright">
<li ng-repeat="item in hpHeadlines" ng-if="$index > 0">
...
</li>
</ul>
Use $first for this:
<ul class="highlight-topright">
<li ng-repeat="item in hpHeadlines" ng-hide="$first">
...
</li>
</ul>
Or,
<ul class="highlight-topright">
<li ng-repeat="item in hpHeadlines" ng-if="!$first">
...
</li>
</ul>
You can also use the limitTo filter.
As a negative index, begin indicates an offset from the end of input. Defaults to 0.
<li ng-repeat="item in hpHeadlines | limitTo: 1 - hpHeadlines.length">
...
</li>
Maybe I am late to answer this. But just for newbies, I am providing solution. As you want to skip the first record, so you can use either $index or $first for achieving, as shown below:
<li ng-repeat="item in hpHeadlines" ng-if="$index"> OR
<li ng-repeat="item in hpHeadlines" ng-if="!($first)">
As you could see, in both, ng-if is skipping '0' value.
You can use ng-show or ng-if without using it as a filter. It's easier because you know the index you want to skip.
Use filters usually when you want to do something that concerns all the array, such as formatting for instance, or parts that you don't know where they reside.
<ul class="highlight-topright">
<li ng-repeat="item in hpHeadlines" ng-show="$index != 0">
{{item.name}}
</li>
</ul>
JS Fiddle
You can use ng-show instead, like this:
<ul class="highlight-topright">
<li ng-repeat="item in hpHeadlines" ng-show="$index > 0">
...
</li>
</ul>
Or you can use ng-if directive in case you don't want to add invisible dom elements.

How to add a class to specific html elements created by ngRepeat?

I need to add a class to the last <li> in an <li>group created using Angular's ngRepeat. So the code below
<ul>
<li class="columns small-3" ng-repeat="tag in tags">
{{tag}}
</li>
<ul>
creates something like
<ul>
<li>Art</li>
<li>Science</li>
<li>Beauty</li>
<ul>
What I need is for the class .end to be added to the last <li> so the end result would be
<ul>
<li>Art</li>
<li>Science</li>
<li class="end">Beauty</li>
<ul>
How is this possible?
You should be able to do this using a combination of ng-class and $last.
<ul>
<li class="columns small-3" ng-class="{'end':$last}" ng-repeat="tag in tags">
{{tag}}
</li>
<ul>
Try with ng class with a condition on the index

How can I use the same piece of html with different data in AngularJS without duplication?

I need the following:
<ul>
<li data-ng-repeat="person in row1">
<img data-ng-src="img/{{person.code}}.jpg">
<h3>{{person.name}}</h3>
<p>{{person.description}}</p>
</li>
</ul>
<ul>
<li data-ng-repeat="person in row2">
<img data-ng-src="img/{{person.code}}.jpg">
<h3>{{person.name}}</h3>
<p>{{person.description}}</p>
</li>
</ul>
but I don't want to duplicate the html - I want to be able to pass in the two different arrays (row1 and row2) to ng-include or something similar. How can I do this in AngularJS?
You can give the array of rows to another model and use ng-repeat in ul tag
In your controller:
$scope.rows = [row1, row2];
In your template:
<ul ng-repeat="row in rows">
<li ng-repeat="person in row">
<img data-ng-src="img/{{person.code}}.jpg">
<h3>{{person.name}}</h3>
<p>{{person.description}}</p>
</li>
</ul>

AngularJS Dropdown ng-if on ng-repeat element

I'm building a twitter bootstrap list using angular's ng-repeat:
<ul class="dropdown dropdown-menu">
<li class="menuOption" ng-repeat="option in options">
<a data-ng-click="option.value>0 ? foo() : goo()">
{{option.label}}
</a>
</li>
</ul>
Now, I want to include a divider (<li class="divider"></li>) inside this list. It should be before the last element in the list (which is also indicated by option.value with a negative value, which is probably an easier indication).
My problem is that since the ng-repeat iteration is on the li element itself, I couldn't find a way to use ng-if on this element.
Try ng-repeat-start and ng-repeat-end:
<ul class="dropdown dropdown-menu">
<li class="menuOption" ng-repeat-start="option in options" ng-if="option.value>0">
<a data-ng-click="option.value>0 ? foo() : goo()">
{{option.value}}
</a>
</li>
<li class="divider" ng-repeat-end ng-if="option.value<0">
option < 0 ({{option.value}})
</li>
</ul>
DEMO

Resources