AngularJS: what is the usage of track by with ng-repeat - angularjs

i am new in angular and i go through this doc https://docs.angularjs.org/api/ng/directive/ngRepeat but do not understand the objective of track by clause using with ng-option.
here are few usage
<div ng-repeat="n in [42, 42, 43, 43] track by $index">
{{n}}
</div>
<div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)">
{{n}}
</div>
<div ng-repeat="model in collection track by model.id">
{{model.name}}
</div>
<div ng-repeat="obj in collection track by $id(obj)">
{{obj.prop}}
</div>
<div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id">
{{model.name}}
</div>
track by is something close to order by clause in sql ?
if yes then how can i specify sort order like ASC or DESC ?
in last code track by and orderBy both use....what they are doing. not clear what kind of output will come.
what is difference between orderBy & track by ?
orderBy is filter that i know. so track by is also a filter.
i know i am asking very basic question. basically posting here because things is not clear after reading it from doc. looking for some help and example to understand what track by does ?
how track by is different from orderby in terms of usage ?

By default orderBy will be in ascending order. You can use "reverse" for descending order like
In HTML Template Binding
{{ orderBy_expression | orderBy : expression : reverse}}
In JavaScript
$filter('orderBy')(array, expression, reverse)
If you do need to repeat duplicate items, you can substitute the default tracking behavior with your own using the track by expression.
For example, you may track items by the index of each item in the collection, using the special scope property $index:
`<div ng-repeat="n in [42, 42, 43, 43] track by $index">
{{n}}
</div>`

ngRepeat uses a function to "keep track" of all items in the collection and their corresponding DOM elements
If a new item is added to the collection, ngRepeat will know that all other items already have DOM elements, and will not re-render them.
See the detail
If you have object for ngRepeat and it have any identity field (Like primary key value or Unique value), use it in track by.
Good thing is that if you are re-loading the list, dome will not re-render for existing items but only the new ones.

Related

angular ng-repeat does not reverse the order of elements when $index is reversed

I have written the following code to print the list in descending order in terms of index position.
<div class="item-layout" ng-repeat="item in selectedMenueItems track by $index | orderBy : '-$index'">
</div>
But somehow, it is only printing the data in ascending order only in terms of index. Is there anything missing here?
You got to use the reverse parameter in orderBy filter here.
e.g.
<div ng-controller="AppController as app">
<div ng-repeat="item in app.items | orderBy:'$index':true track by $index ">
{{item}}
</div>
</div>
Here's a working fiddle.
change this line orderBy : '-$index' to orderBy : '$index' : true to get the desired result.
Refer the documentation https://docs.angularjs.org/api/ng/filter/orderBy for more better understanding.

What's wrong with my Angular filter?

I have an array of objects and I want to do an ng-repeat | filter on that array.
activities is my array and inside the object there is a CreatePeriod property.
<li ng-repeat="activity in activities track by $index | filter:{CreatePeriod:'Today'}:true">
The property does exist, {{activity.CreatePeriod}} displays the correct information. The CreatePeriod can be 'Today', 'Yesterday', and 'Older'.
What am I missing in my filter to only show an activity if its CreatePeriod == 'Today'
track by must come after all filters per the docs
Note: track by must always be the last expression:
try:
<li ng-repeat="activity in activities | filter:{CreatePeriod:'Today'}:true track by $index">

Looping in AngularJS not working

I'm trying a simple AngularJS looping using 'ng-repeat' directive as below :
<div ng-app="" ng-init="numbers=[1,3,5,2]">
<ul>
<li ng-repeat="item in numbers">{{ item }}</li>
</ul>
</div>
The result of this is as below, which is perfect
1
3
5
2
However, when I change the 'numbers' array like this
<div ng-app="" ng-init="numbers=[1,3,5,2,2]">
being the rest as is, it does not work.
The only change I have made is that I've added one more item in the 'numbers' array '2'. The issue I figured out is whenever an item is repeated in the array ( '2' in this case ), the problem occurs.
The console log I noticed is like below
Error: [ngRepeat:dupes] http://errors.angularjs.org/1.3.14/ngRepeat/dupes?p0=item%20in%20numbers&p1=number%3A2&p2=2
at Error (native)
at http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js:6:417
at http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js:232:494
at Object.fn (http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js:122:53)
at l.$get.l.$digest (http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js:123:138)
at l.$get.l.$apply (http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js:126:58)
at http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js:17:479
at Object.e [as invoke] (http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js:36:315)
at d (http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js:17:400)
at tc (http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js:18:179)
Also, if the array is of string type values, the same problem persists too.
For example, <div ng-app="" ng-init="names=['Bishnu', 'Sagar', 'John', 'Bishnu']">
in this case also I'm facing the same issue.
This behavior of AngularJS is very strange.
Does anyone know the reason, and how to resolve?
Try this...
The ngRepeat directive instantiates a template once per item from a collection. Each template instance gets its own scope, where the given loop variable is set to the current collection item, and $index is set to the item index or key.
ngRepeat makes the corresponding changes to the DOM
When an item is added, a new instance of the template is added to the DOM.
When an item is removed, its template instance is removed from the DOM.
When items are reordered, their respective templates are reordered in the DOM.
By default, ngRepeat does not allow duplicate items in arrays. This is because when there are duplicates, it is not possible to maintain a one-to-one mapping between collection items and DOM elements.
If you do need to repeat duplicate items, you can substitute the default tracking behavior with your own using the track by expression
<div ng-repeat="n in [42, 42, 43, 43] track by $index">
{{n}}
</div>
Refer:https://docs.angularjs.org/api/ng/directive/ngRepeat
As per the Angular Docs Duplicates are not allowed. You need to use 'track by' expression to specify unique keys.
Created this Plnkr for your reference
<div ng-app="" ng-init="numbers=[1,3,5,2,2]">
<ul>
<li ng-repeat="item in numbers track by $index">{{ item }}</li>
</ul>
</div>
You need to use track by $index to iterate through duplicate entry as well.
you can try like this
<div ng-repeat="value in [4, 4] track by $index">{{value}}</div>

Angularjs ng-repeat: how to detect a collection vs an array

I need to repeat over either a collection or an array, depending on the situation.
How Would I do that, provided the following approach causes "ngRepeat: dupes", code snippet as follows:
<div ng-if="cause.length" ng-repeat="k in cause">
{{k}}
</div>
<div ng-if="!cause.length" ng-repeat="(k,v) in cause">
{{k}}
</div>
add track by $index to end of ng-repeat declaration any you will not get any Dupes error...
<div ng-if="cause.length" ng-repeat="k in cause track by $index">
{{k}}
</div>
<div ng-if="!cause.length" ng-repeat="(k,v) in cause track by $index">
{{k}}
</div>
UPDATE
If no tracking function is specified the ng-repeat associates elements by identity in the collection. It is an error to have more than one tracking function to resolve to the same key. (This would mean that two distinct objects are mapped to the same DOM element, which is not possible.)
and this explanation from angularjs documentation explains why it resolve this error...

Filtering an Angular 1.2 ng-repeat with "track by" by a boolean property

I'm trying to filter some list items based on the value of a boolean property, but no matter what I do the entire list is always displayed. A few of the the things I've tried have been so broken that nothing displays, but that's neither here nor there. I can't get my filtering working as desired:
$scope.attendees = [
{"firstname":"Steve", "lastname":"Jobs", "arrived":true, "id":1}
,{"firstname":"Michelle", "lastname":"Jobs", "arrived":false, "id":2}
,{"firstname":"Adam", "lastname":"Smith", "arrived":true, "id":3}
,{"firstname":"Megan", "lastname":"Smith", "arrived":false, "id":4}
,{"firstname":"Dylan", "lastname":"Smith", "arrived":false, "id":5}
,{"firstname":"Ethan", "lastname":"Smith", "arrived":false, "id":6}
];
Using the following ng-repeat filtering:
<ul>
<li ng-repeat="person in attendees track by person.id | filter:arrived:'false'">
{{person.lastname}}, {{person.firstname}}
</li>
</ul>
I feel like I've tried every permutation that I can find referenced, most of which came from various StackOverflow search results:
filter:'arrived'
filter:arrived
filter:'person.arrived'
filter:person.arrived
filter:{arrived:true}
filter:{arrived:'true'}
filter:{person.arrived:true}
filter:{person.arrived:'true'}
I've also tried creating a custom filter function:
$scope.isArrived = function(item) {
return item.arrived;
};
And applying it thusly:
filter:isArrived
filter:'isArrived'
filter:{isArrived(person)}
filter:isArrived(person)
filter:'isArrived(person)'
None of these seems to work. What am I missing?
Here's a plnkr that demonstrates my problem.
The track by needs to be at the end of the expression:
<li ng-repeat="person in attendees | filter: {arrived: false } track by person.id">
#Gruff's answer is right, but just to give an answer from an official source:
From the Angular ng-repeat docs:
Note: track by must always be the last expression:
<div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id">
{{model.name}}
</div>
It also appear on the "Arguments" part of the docs:
Note that the tracking expression must come last, after any filters,
and the alias expression.

Resources