Quantcast
Channel: User Simon_Weaver - Stack Overflow
Viewing all articles
Browse latest Browse all 116

Answer by Simon_Weaver for angular ngFor trackBy does not work as I expected

$
0
0

Edit: None of this answer has been updated since the new @for syntax came out. So some of the following may not still be relevant or be confusing if you're not familiar with the 'old way'. But in essence it should still apply.


If trackBy doesn't seem to work:

1) Make sure you are using the correct signature for the trackBy function

https://angular.io/api/core/TrackByFunction

interface TrackByFunction<T> {  (index: number, item: T): any}

Your function must take an index as the first parameter even if you're only using the object to derive the 'tracked by' expression.

trackByProductSKU(_index: number, product: { sku: string }){    // add a breakpoint or debugger statement to be 100% sure your    // function is actually being called (!)    debugger;    return product.sku;}

2) Make sure the entire control (that contains the *ngFor) isn't being redrawn, possibly as a side effect of something else.

  • Add <input/> in the control just above your *ngFor loop - (Yes - just an empty text box)
  • Load the page and type something into each of the textboxes (I usually just enter 1, 2, 3, 4...)
  • Add / remove an item from the list - or whatever you need to do to trigger a change
  • If the contents of your textbox disappears then it means you're redrawing the entire container control (in other words your trackBy has nothing to do with your underlying issue).
  • You can put <input/> at each 'level' if you have multiple nested loops. Just type a value into each box, then see which values are retained when you perform whatever action is causing the problem.

3) Make sure the trackBy function is returning a unique value for each row:

<li *ngFor="let item of lineItems; trackBy: trackByProductSKU"><input />    Tracking value: [{{ trackByProductSKU(-1, item) }}]</li>

Display the track by value inside your loop like this. This will eliminate any stupid mistakes - such as getting the name or casing of the track by property incorrect. The empty input element is deliberate

If everything is working properly you should be able to type in each input box, trigger a change in the list and it shouldn't lose the value you type.

4) If you cannot determine a unique value, just return the item itself. This is the default behavior (from trackByIdentity) if you don't specify a trackBy function.

// angular/core/src/change_detection/differs/default_iterable_differ.tsconst trackByIdentity = (index: number, item: any) => item;export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChanges<V> { constructor(trackByFn?: TrackByFunction<V>) {    this._trackByFn = trackByFn || trackByIdentity; }

5) Don't accidentally return null or undefined!

Let's say you're using product: { sku: string } as your trackBy function and for whatever reason the products no longer have that property set. (Maybe it changed to SKU or has an extra level.)

If you return product.sku from your function and it's null then you're going to get some unexpected behavior.


Viewing all articles
Browse latest Browse all 116

Trending Articles