Manual change detection in Angular for performance

There are special use cases where you might need to customize change detection in Angular applications for performance reasons. The majority of the time, default change detection is the correct way to go. I’ve worked with several customers recently who were using very large datasets within Angular 8 components and this caused some interesting UI slowdowns as a side-effect.

Fortunately, and unsurprisingly, Angular does allow you modify change detection, for example ChangeDetectionStrategy.OnPush and ChangeDetectorRef. In both cases that I worked on, my recommendation was to lean towards ChangeDetectorRef in order to provide granular, explicit control over when a component updated to reduce performance impacts on the UI.

In one case, the change detection needed to be slowed down and it was suitable to run it on a timer loop. Here’s the pseudo-code, and there are plenty of similar examples on the internet including in the Angular documentation:

import { Component, ChangeDetectorRef, . . .  } from '@angular/core';

constructor(private changeDetector: ChangeDetectorRef) {
    changeDetector.detach();
    setInterval(() => {
      this. changeDetector.detectChanges();
    }, 5000);
  }

In the other use case, the change detection only needed to happen when an Observable from a service was updated. That implementation used a pattern similar to this pseudo-code:

import { Component, ChangeDetectorRef, OnInit } from '@angular/core';

constructor(private stateMgmtService: StateMgmtService, private changeDetector: ChangeDetectorRef) {}

public messages$: Observable<MySpecialArray[]>
public list: Subscription:
public data: any[] = [];

ngOnInit() {
   this.changeDetector.detach(); 
   this.messages$ = this.stateMgmtService.getSomeData();
   this.list = this.message$.subscribe({
      next: x => {
         // . . . do some calculations against x
         this.data = x;
         // Only detect changes on next
         this.changeDetector.detectChanges();
      }
   })

}

And here’s the component.html:

<!-- https://material.angular.io/cdk/scrolling/overview -->
<cdk-virtual-scroll-viewport [itemSize]="8" class="point-viewport">
  <div *cdkVirtualFor="let location of data”>
    {{location}}
  </div>
</cdk-virtual-scroll-viewport>

Caveat Emptor. There are technical debt issues when you implement manual control on Angular change detection. Instead of a nice, loosely coupled approach to handling UI updates, you step into potentially creating inconsistencies between how different components handle updates and this adds complexity to your application. This can also affect how you write unit tests and can introduce unforeseen bugs. With all that said, sometimes you have to make decisions based on your unique requirements and you have to take the best approach for your circumstances.