7

Since recently, the Angular styleguide-lint-extender "Codelyzer" is throwing warnings when you do not have a trackBy-function implemented on every *ngFor. I am wondering why this is considered an issue at all.

  • In this blog the example of implementing trackBy comes down to trackByFn(index, item) { return index;} // or item.id. If I switched from index to item.id, how would this make my app faster? when it comes to array insertions or deletions, index is the most straightforward thing that matters. Why should the [ngFor]-directive compare object identity values instead?
  • in the module ng_for_of.d.ts I can find _trackByFn. So I assume, a return index-trackBy is the default configuration anyway? Then why is it considered a good practice to implement it explicitly?

Now personally, I do have a big collection (array) in my app, and it lies in a redux store. It can only be replaced by an empty array or new items can get added to it, eg:

return {...state, myArray: [...state.myArray, ...newItems]}),

but never moved or deleted. Would it make sense for me to track by item.id instead of index? Should I really implement a return index;-function in every component with an *ngFor?

Sujatha Girijala
  • 953
  • 7
  • 20
Phil
  • 5,442
  • 6
  • 36
  • 70

1 Answers1

8

*ngFor tracks the items by object identity and tries to avoid re-render of elements when the iterated array is updated for items in the array that where in the array already before the update.

If the array contains primitive values (string, boolean, number), then *ngFor can't identify them after they were modified.

An *ngFor over a list of strings like

items = ['foo', 'bar', 'baz'];
<input *ngFor="let item of items" [(ngModel)]="item">

would cause weired behavior when the value is modified in the rendered <input> because after each keyboard input the value would change and *ngFor would loose track where the item was rendered before and therefore remove the input for the value before the change and add it for the value after the change. This causes the input to lose focus and can cause the input to change position.

To fix this you can instruct *ngFor to track by index instead of by identity.

See also

Also for objects it can be useful, for example if you want *ngFor to track items by an id property. This is useful for example if immutable values are used and an item of the list was modified - which means replaced by a new object instance with the same value for the id property, but some other properties changed. Instructing *ngFor to track by id doesn't cause the item to be removed and re-added to the DOM.

*ngFor not properly recognizing modified items would also cause animations like shown in this Plunker example (from How can I animate *ngFor in angular 2?) to break (animated too often).

Günter Zöchbauer
  • 490,478
  • 163
  • 1,733
  • 1,404
  • 1
    Thank you for this in-depth explanation. It looks like I don't have use case where trackBy would be useful yet. I will just set the codelyzer flag to false. I find it strange that is not false by default, considering ngFor is used everywhere and those cases are rather very specific. – Phil Dec 16 '17 at 09:08
  • 1
    I don't think it's common to use `trackBy`. I think the cases mentioned above are rather rare. – Günter Zöchbauer Dec 16 '17 at 09:12