Angular.js: custom directive inside ng-repeat doesn't update once using track by index

Created on 4 Nov 2016  路  6Comments  路  Source: angular/angular.js

It seems the custom directive used in the ngRepeat with "track by index" will not got update?
I have create a plunk for this:

http://plnkr.co/edit/MBleogyCukFh4WEZWCts?p=preview

Once you remove the "track by index", everything works fine!

If it is not a bug, please guide me where the doc has introduced this part. Thanks!

Most helpful comment

This is expected behavior for track by $index. The point of tracking items is to re-use the same DOM element + scope for the same item, so Angular doesn't have to go through the whole compiling/linking cycle for tracked elements. From the docs:

To minimize creation of DOM elements, ngRepeat uses a function to "keep track" of all items in the collection and their corresponding DOM elements. For example, if an item is added to the collection, ngRepeat will know that all other items already have DOM elements, and will not re-render them.

The default tracking function (which tracks items by their identity) 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.

[...]

Should you reload your data later, ngRepeat will not have to rebuild the DOM elements for items it has already rendered, even if the JavaScript objects in the collection have been substituted for new ones.

So, by using track by $index, you are telling Angular that the _nth_ list item is always the same (because it's index is always _n_), so it will not create and new DOM element, compile the template, create a new scope, link the element with the scope etc. It will re-use the already compiled/linked _nth_ DOM element for the _nth_ list item (even if the list changes).

That's what tracking by $index is about.

All 6 comments

@gkalpak is great at explaining this behavior. We should put it in the docs somehow ...

This is expected behavior for track by $index. The point of tracking items is to re-use the same DOM element + scope for the same item, so Angular doesn't have to go through the whole compiling/linking cycle for tracked elements. From the docs:

To minimize creation of DOM elements, ngRepeat uses a function to "keep track" of all items in the collection and their corresponding DOM elements. For example, if an item is added to the collection, ngRepeat will know that all other items already have DOM elements, and will not re-render them.

The default tracking function (which tracks items by their identity) 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.

[...]

Should you reload your data later, ngRepeat will not have to rebuild the DOM elements for items it has already rendered, even if the JavaScript objects in the collection have been substituted for new ones.

So, by using track by $index, you are telling Angular that the _nth_ list item is always the same (because it's index is always _n_), so it will not create and new DOM element, compile the template, create a new scope, link the element with the scope etc. It will re-use the already compiled/linked _nth_ DOM element for the _nth_ list item (even if the list changes).

That's what tracking by $index is about.

@gkalpak Thanks for the explaination! :)

Using track by, any custom scope manipulation I did in a directive's link function, gets removed when the element is recycled. ie: a list of articles on a page, change up the filtering for the list and any instances that existed between the two now have custom scope properties removed that were generated in the original run of the link function. So... seems to me that angular isn't preserving the scope when using track by :(

@ramseyfeng Thank you so much you saved me, It told me the whole day to fix this, when I removed the "track by index" it works fine.

@olivezz np :)

Was this page helpful?
0 / 5 - 0 ratings