0

I have a simple list of <div> generated using *ngFor directive, something like

<div *ngFor="let item of items() | async ; let i=index" 
  [ngClass]="getItemClass(i)"
  (click)="itemClick(i)"</div>

As you see, the list is filled asynchronously any time the Observable returned by the method items() emits. In my case such Observable is a ReplaySubject, if this can be of any use to know. I then define the classes to be applied to each <div> element using the method getItemClass.

I want also to react to a click event on each item using the method itemClick(i: number).

Problem

Any time a div element is clicked, i.e. any time the itemClick(i: number) method is run, it seems that the entire list of <div>s is rebuilt. I come to this conclusion observing that any time a div element is clicked, also the items() method is run.

Question

Is it possible to avoid rebuilding the list when one of the <div> elements is clicked? I have already set changeDetection to be OnPush but it does not seem to solve my problem.

Picci
  • 16,775
  • 13
  • 70
  • 113
  • Possible duplicate of [Angular change detection process repaint the dom](https://stackoverflow.com/questions/45311502/angular-change-detection-process-repaint-the-dom) – Christopher Peisert Dec 30 '18 at 17:14

1 Answers1

5

Yes, even with onPush the change detection will be triggered. And that will cause the evaluation of the index() method. This is sort of lost in the documentation. Any event that will bubble to the zone.js code, will trigger a change detection on every component from the root to that component. Setting onPush will not prevent that. In order to avoid recreating the components in the loop you need to add trackBy.

In your html template:

<li *ngFor="let hr of heroes;trackBy: trackByFn">{{hr.name}}</li>

and then in the component you have to define the function:

trackByFn(index, item) {    
   return item.id  
}

Item is passed from the loop. and if the id of that item will not change the item will not be recreated.

Also I would get rid of the index() method and replace it with the variable. As I mentioned it will be evaluated every time the change detection runs. I wrote an article about some performance tricks that you can do to speed up your application.

https://medium.com/12-developer-labors/some-things-i-wish-i-knew-before-i-start-to-work-with-angular-part-2-performance-47cd834dc409

piotr szybicki
  • 1,532
  • 1
  • 11
  • 12
  • Thanks, it make perfect sense. Do you know whether you can get the same result using NgZone? – Picci Dec 29 '18 at 23:00
  • There is but it is not worth it. It basically boils down to disabling NgZone in angular and then triggering change detection manually. It is hard as there is a lot of corner cases to consider. Pretty cool article on the subject: https://blog.angularindepth.com/do-you-still-think-that-ngzone-zone-js-is-required-for-change-detection-in-angular-16f7a575afef – piotr szybicki Dec 30 '18 at 10:09