112

I'm having difficulties understanding how the track by expression of ng-repeat in angularjs works. The documentation is very scarce: http://docs.angularjs.org/api/ng/directive/ngRepeat

Can you explain what the difference between those two snippets of code is in terms of databinding and other relevant aspects?

with: track by $index

<!--names is an array-->
<div ng-repeat="(key, value) in names track by $index">
  <input ng-model="value[key]">                         
</div>

without (same output)

<!--names is an array-->
<div ng-repeat="(key, value) in names">
   <input ng-model="value[key]">                         
</div>
isherwood
  • 58,414
  • 16
  • 114
  • 157
Jonathan Grupp
  • 1,832
  • 3
  • 16
  • 12

3 Answers3

105

You can track by $index if your data source has duplicate identifiers

e.g.: $scope.dataSource: [{id:1,name:'one'}, {id:1,name:'one too'}, {id:2,name:'two'}]

You can't iterate this collection while using 'id' as identifier (duplicate id:1).

WON'T WORK:

<element ng-repeat="item.id as item.name for item in dataSource">
  // something with item ...
</element>

but you can, if using track by $index:

<element ng-repeat="item in dataSource track by $index">
  // something with item ...
</element>
T J
  • 42,762
  • 13
  • 83
  • 138
nilsK
  • 4,323
  • 2
  • 26
  • 40
  • 3
    thanks for your answer! But surely duplicate identifiers aren't the only use-case. Also I'd like to know what's happening 'under the hood'. – Jonathan Grupp Mar 31 '14 at 12:39
  • 2
    well, that's easy: just have a look at the [code](https://github.com/angular/angular.js/blob/master/src/ng/directive/ngRepeat.js#L3), it's all open source ;) – nilsK Mar 31 '14 at 13:22
  • 4
    This question is old but I still think this might help understand much better http://www.bennadel.com/blog/2556-using-track-by-with-ngrepeat-in-angularjs-1-2.htm short version of the explanation here https://docs.angularjs.org/error/ngRepeat/dupes – Annapoorni D Jul 10 '14 at 07:35
  • 3
    One more thing to take into account is that if you can use track by key, you will get better performance (blog.500tech.com/is-reactjs-fast ). This feature allows you to associate a JavaScript object with an ngRepeat DOM (Document Object Model) node using a unique identifier. With this association in place, AngularJS will not $destroy and re-create DOM nodes unnecessarily. This can have a huge performance and user experience benefit (http://www.bennadel.com/blog/2556-using-track-by-with-ngrepeat-in-angularjs-1-2.htm). – Braulio Jun 13 '15 at 08:54
  • I have a list of 700 odd items. Render time went from 4 seconds to 100ms. Track by should be used for all ngRepeat's based on data sourced from rest. – Patrick Nov 03 '16 at 01:04
  • I am able to use `$index` without using track by. How? – Astitva Srivastava May 24 '17 at 07:15
  • @AstitvaSrivastava $index is property of the ngRepeat directive. https://docs.angularjs.org/api/ng/directive/ngRepeat Adding `track by $index` just forces ngRepeat to use $index as identifier. – nilsK May 24 '17 at 10:37
  • @nilsK Thanks. I got it! – Astitva Srivastava May 24 '17 at 10:58
  • `` - This doesn't work even if the values in "dataSource" aren't dups it just throws a syntax error.. – BornToCode Jan 03 '18 at 17:08
  • @BornToCode thats why i wrote 'WON'T WORK' in uppercase and bold above the text ^^ please use the second example with track by $index – nilsK Jan 04 '18 at 08:37
  • Yes but the fact that it doesn't work doesn't illustrate your point. It doesn't work because of corrupted syntax, not because of dups. – BornToCode Jan 04 '18 at 13:24
  • Just an example of why this might matter regarding avoiding errors: https://jsfiddle.net/8gy4tnrL/6/ – Jimmyt1988 Feb 11 '22 at 15:19
  • @Jimmyt1988 This post and answer is from 2014. AngularJs is deprecated. – nilsK Feb 11 '22 at 15:25
  • @nilsK angularJs is still used by entire products. So it is relevant to those; unfortunate, nonetheless. It's why I found myself here. – Jimmyt1988 Feb 14 '22 at 16:11
65

a short summary:

track by is used in order to link your data with the DOM generation (and mainly re-generation) made by ng-repeat.

when you add track by you basically tell angular to generate a single DOM element per data object in the given collection

this could be useful when paging and filtering, or any case where objects are added or removed from ng-repeat list.

usually, without track by angular will link the DOM objects with the collection by injecting an expando property - $$hashKey - into your JavaScript objects, and will regenerate it (and re-associate a DOM object) with every change.

full explanation:

http://www.bennadel.com/blog/2556-using-track-by-with-ngrepeat-in-angularjs-1-2.htm

a more practical guide:

http://www.codelord.net/2014/04/15/improving-ng-repeat-performance-with-track-by/

(track by is available in angular > 1.2 )

Nuriel
  • 3,731
  • 3
  • 23
  • 23
8

If you are working with objects track by the identifier(e.g. $index) instead of the whole object and you reload your data later, ngRepeat will not rebuild the DOM elements for items it has already rendered, even if the JavaScript objects in the collection have been substituted for new ones.

ram1993
  • 979
  • 1
  • 9
  • 13
  • any reference that proves this? – azerafati Jul 17 '17 at 06:26
  • 1
    is there a way to force re-rendering? or any other work around? i have not found this mentioned anywhere but i believe this is what creating a mess for me and i already wasted a lot of time. – NeverGiveUp161 Sep 04 '18 at 10:39
  • 2
    either don't use track by or change the unique identifier on changing object. Note you cant change $index, its recommended not to use $index instead use identifier unique to object (e.g. id) – ram1993 Sep 26 '18 at 08:14