Turning Off Change Detection for Performance in Ionic

Angular’s change detection is usually quite great, but I found a case where it’s performance was terrible on a page that contained a long vertical list of horizontal lists of images (think the soundcloud home page where there are lots of categories of songs each represented by an image).

The big problem was that with potentially hundreds of images i wanted to only load images that were within the viewing area.

For this use case i used IntersectionObserver which does just that. One, thing to note is that it isn’t available on iOS and requires a polyfill and requires the polyfill to be tweaked with POLL_INTERVAL of say 1000.

Next step was to turn off change detection on the page by adding this to @component:

changeDetection: ChangeDetectionStrategy.OnPush,

and this to the constructor:

private cdr: ChangeDetectorRef

then this to ngOnInit():

this.cdr.detach();

Without change detection i needed to know when there were changes to my view or interaction from the user. For the interaction i wanted to know whenever the user scrolled or touched the app. In Ionic 4 I can get the scroll by doing the following:

<ion-content #content>

then adding the ViewChild for it:

@ViewChild('content')
content: Content;

then in my ionViewDidEnter():

const element = await this.content.getScrollElement();
this.scrollSubscription = merge(fromEvent(window, 'touchstart'), fromEvent(element, 'scroll'), interval(1000))
.pipe(debounceTime(300),startWith(0))
.subscribe((e) => { this.cdr.detectChanges(); });

Note: i’ve imported some stuff from rxjs here:

import { fromEvent, interval, merge } from 'rxjs';
import { debounceTime, startWith } from 'rxjs/operators';

So what does this magic do?

The “getScrollElement” will get the HTML element of the ion-content that scrolls. Then the “fromEvent” gets an observable of events whenever the user scrolls in that content. The “touchstart” code gets events whenever the user touches the screen (eg to slide left/right) and the interval operator gives an event every 1 second. The debounceTime operator ensures i don’t get flooded with events. Finally, the detectChanges causes Angular to manually check for changes in the view.

The end result is that whenever the user interacts with the page we efficiently check for changes and re-render.