Angular change detection OnPush with examples

Da Feng
4 min readOct 29, 2019
Photo by Chris Lawton on Unsplash

Change detection

By default Angular runs change detection automatically for us. It checks for all components and child components when anything from a click event to an Input binding changes, which is not quite efficient with a big and complicated app. ChangeDetectionStrategy.onPush on the other hand only run change detection when we specify it to, hence is more efficient.

This article is focusing on ChangeDetectionStragegy.onPush. By undertanding all the behavior and senarios of onPush, it will be equally safe but more efficient to use onPush than the default change detection strategy.

One thing in mind is that, when change detection runs (no matter it’s a default strategy or onPush strategy), it will go down to the component tree and check for the changes of current component and also it’s child components. So to better undertand change detection, all the following test will have two sinario: change happen in parent component (and then passed to child via Input binding), and change happen in child component( what happen if we change input value or reference in the current component?)

Default change detection strategy

Any event change (every event bound to using (foo)=”…”), @Input binding changes, variable used in template changes will trigger change detection.

First lets check Input property binding. By default, input property is not checked by reference, any property change will be detected, and change detection will run for this component and child component.

Onpush change detection need to notice

When using onPush change detection, there are following senarios we need to know that change dection won’t be triggered and we need manually trigger angular change detection if needed.

First, @Input binding and variables

Input Bindings : @Input object value change won’t trigger change detection(example 2 & 3)

Other variables: all variables used in the template change, won’t trigger change detection(example 2 & 3)

Example 2 (parent default, child onPush), shows that change Input object value won’t trigger change detection for current component, since parent compoent still contains the reference of user, it will update parent component.

Triggered only when @Input object reference has been changed from parent component and passed to child ( example 3–2)

Example 3–2 (parent default, child onPush), change the reference in Parent and pass in to child (child @Input binding reference changed from outside), this is the only case that change detection will run on a component with onPush change detection. This scenario happens quite often in our daily development process, and require us to manually run a changeDetectorRef.detectChanges() to pick up the changes.

Also, in this senario, if we change parent component to use OnPush also, There won’t be any change detection, since user object is changed in Parent, with onPush, parent won’t pick up the change, hence wont trigger child component to run change detection.

Second, Event binding using (foo)=”bar()” or @HostListener()

Component with OnPush still able to detect chanegs, if any event binding of this component template is triggered. Any event binding like (foo) = “bar()” or using HostListener().

Example 4 (parent default, child onPush). To demo this senario, child component change User firstname after 3 sec, won’t trigger change detection as discussed in Example 2, then by clicking on an empty button, Angular will trigger change detection on child component and update its view.

Third, use async Pipe, not manual subscribe

When developing with change detection Onpush, the best practice is to try to make the variable that can change in the component into an observable, and use async pipe in the html template. angular will automatically trigger change detection when an async pipe has a new value. While on the other side, it’s common when developing a component with OnPush, and manually subscribe observable. In this case change detection won’t be triggered, and we need manually run changeDetectorRef.detectChanges().

Example 5 (parent default, child onPush).child component changed user reference in a subscription, this won’t trigger change detection. The quick way to solve this problem is inside the subscription run this.cdr.detectChanges().

Example 6 (parent default, child onPush).child component changed user by using async-pipe, this is prepered and will trigger change detection.

Conclusion

Keep in mind of following details of OnPush change detection, will make your app working transparently as default, but more efficient!

1. With Onpush, Angular only compare change of variable by reference. and only @Input changed and passed from parent, will auto trigger change detection.

2. Any event binding using (foo)=”bar()” or @HostListener() will trigger change detection automatically

3. Async pipe will trigger change detection automatically, so try to use more async pipe, and avoid manully do the subscription in component, otherwise a changeDetectorRef.detectChanges() is required.

References

Angular OnPush Change Detection and Component Design — Avoid Common Pitfalls

A Comprehensive Guide to Angular onPush Change Detection Strategy

Stack overflow question

Git hub issue

--

--

Da Feng

I am a software developer, write blogs about Typescript, Angular, React, vue, Front-end technologies, also share some of my life stories and good memories here.