1

For performance issues i'm trying to make only childrens that has changed reload. I have this array of objects

public table: any[] = [
    {id:1, data:"data1"},
    {id:2, data:"data2"},
    {id:3, data:"data3"},
    {id:4, data:"data4"},
  ]

Which is rendered like this:

<app-cell *ngFor="let cell of table" [cellData]="cell"></app-cell>

Once i modify a element, for example the one with "id:2" :

change() {
    this.table[2].data = 'Data3 but has changed';
  }

All the child components in ngFor loop are reloaded. I have tried to use trackBy but my knowledges are not sufficent to achieve what I'm trying to do.

How can I only make the ones who changed reload ?

Thanks in advance,

Nicolas.

R. Richards
  • 24,603
  • 10
  • 64
  • 64
NCoding
  • 29
  • 4
  • Try OnPush Change Detection within app-cell – MoxxiManagarm Jul 26 '22 at 12:55
  • Angular is smart enough to do that for you if you mind immutability (providing the same array object with one replaced element). It uses a default implementation of `ngForTrackBy` where it checks the objectId of the elements in the collection. You can provide your own implementation of ngForTrackBy to check for changes in a certain field or fields. – H3AR7B3A7 Jul 26 '22 at 12:55
  • @H3AR7B3A7 No man. It doesn't work that way. What Angular tracks are object identities, and those change along with the reference, even though the object's properties have the same values. Read some more here: https://angular.io/api/common/NgForOf#change-propagation – Octavian Mărculescu Jul 26 '22 at 13:02
  • @OctavianMărculescu What doesn't work what way? Could you be more specific? – H3AR7B3A7 Jul 26 '22 at 13:06
  • I was replying to your unedited comment with "smart enough to build itself a default trackBy function that uses objectId". – Octavian Mărculescu Jul 26 '22 at 13:09
  • Well it is smart enough, if you use some sort of state management. – H3AR7B3A7 Jul 26 '22 at 13:10
  • @MoxxiManagarm Could work but in child component, there isn't any function that triggers when Input data has changed. – NCoding Jul 26 '22 at 13:12
  • @H3AR7B3A7 That's not what the documentation says. It doesn't use a default implementation of trackBy, it is just comparing object references, no shenanigans. – Octavian Mărculescu Jul 26 '22 at 13:14
  • You can pass the observable with an async pipe, and don't subscribe to the result. But I'm going to assume other stuff might break, so just writing your own `ngFOrTrackBy` is the easiest solution. – H3AR7B3A7 Jul 26 '22 at 13:16
  • @OctavianMărculescu That's what I meant, but you're right it's the object reference, not the objectId. I see how what I wrote might be confusing. It still conveys all the needed information though. – H3AR7B3A7 Jul 26 '22 at 13:17
  • @H3AR7B3A7 I cannot figure out what are you trying me to figure out. Actually Angular is clearly refreshing every single child even if he has not changed. – NCoding Jul 26 '22 at 13:44
  • Does this answer your question? [How to use \`trackBy\` with \`ngFor\`](https://stackoverflow.com/questions/42108217/how-to-use-trackby-with-ngfor) – H3AR7B3A7 Jul 26 '22 at 13:51
  • @H3AR7B3A7 Already seen this one, but sadly no.. – NCoding Jul 26 '22 at 14:17

1 Answers1

1

You should keep trying to use the trackBy function. My suggestion is to combine both id and data in a string and return that:

trackByIdAndData(index: number, item: any): string {
  return `${item?.id}_${item?.data}`;
}

And apply this track by function to your ngFor:

<app-cell *ngFor="let cell of table; trackBy: trackByIdAndData" [cellData]="cell"></app-cell>
Octavian Mărculescu
  • 4,312
  • 1
  • 16
  • 29
  • Thanks for your answer but as i can see, It does not solves my problem. In my cell component I logged the id of the cell everytime view is re rendered. And using your track by id function. Every id's are printed to console. Then I assume it does not work. – NCoding Jul 26 '22 at 14:16
  • Combine it with `changeDetection: ChangeDetectionStrategy.OnPush` on the `app-cell` component. – Octavian Mărculescu Jul 26 '22 at 14:28