Angular.js: ng-repeat track by $index with custom directive

Created on 15 Nov 2017  ·  3Comments  ·  Source: angular/angular.js

I'm submitting a ...

  • [ ] bug report
  • [ ] feature request
  • [X] other

Current behavior:

After filtering and array that is being repeated and has a custom directive in each repeated element that has an isolated scope. The custom directive is not reinitiated.

I assume this is expected behaviour but can be confusing and should be made clearer as to what putting track by $index can do to your custom directives.

Expected / new behavior:
NA

Minimal reproduction of the problem with instructions:

  1. create ng repeat with a custom directive inside (include track by $index)
  2. using $filter on the repeat array filter the array and you will notice the data in the directive will not reflect the new filtered array

AngularJS version: 1.5.10

Browser: all

Anything else:

Could the docs be made more clear between the what Angular is doing when using track by and that using $index with custom directives is not going to work.

Check my response here for a break down of the issue
https://stackoverflow.com/a/47310734/2536454

ngRepeat docs

Most helpful comment

I believe this is indeed expected behavior. Quoting the docs:

When items are reordered, their respective templates are reordered in the DOM.

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.

In such cases (when using track by $index), the nth DOM element will always be matched with the nth item of the array, so the bindings on that element will not be updated even when the corresponding item changes, essentially causing the view to get out-of-sync with the underlying data.

Putting it all together, what happens is that tracking by $index, tells ngRepeat that the first item is always the same (since it has the same index). ngRepeat does not know there underlying data has changed, because you tell it that as long as the index is the same, the item is the same (in prractice, tracking by $index is rarely useful).

Since ngRepeat things that the item is the same, it will not re-create the DOM, but keep the existing one (bound to the existing scope etc). And therefore any directives that appear on the tempate will not be re-compiled (since compilation/linking happens only when "stamping out" a new instance).

But I admit we are not doing a terribly good job explaining that in the docs :grin:
@michael-letcher, would you be interested in submitting a PR to improve the docs?

All 3 comments

Any chance u can be a bit more specific (or provide a reproduction sample)?
You can start from this plunkr to reproduce the problem, as it seems to work fine for me.

https://plnkr.co/edit/dxzgjmE5MEA0bwQdckVS?p=preview

I believe this is indeed expected behavior. Quoting the docs:

When items are reordered, their respective templates are reordered in the DOM.

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.

In such cases (when using track by $index), the nth DOM element will always be matched with the nth item of the array, so the bindings on that element will not be updated even when the corresponding item changes, essentially causing the view to get out-of-sync with the underlying data.

Putting it all together, what happens is that tracking by $index, tells ngRepeat that the first item is always the same (since it has the same index). ngRepeat does not know there underlying data has changed, because you tell it that as long as the index is the same, the item is the same (in prractice, tracking by $index is rarely useful).

Since ngRepeat things that the item is the same, it will not re-create the DOM, but keep the existing one (bound to the existing scope etc). And therefore any directives that appear on the tempate will not be re-compiled (since compilation/linking happens only when "stamping out" a new instance).

But I admit we are not doing a terribly good job explaining that in the docs :grin:
@michael-letcher, would you be interested in submitting a PR to improve the docs?

what

Was this page helpful?
0 / 5 - 0 ratings