One of the most loved features of Angular is its ability to automatically update the view whenever data changes.

This is thanks to the default Angular’s change detection mechanism, which constantly checks for changes in data and updates the view accordingly. While this is great for most use cases, it is possible to manually detect changes for performance optimisation.

Let’s explore change detection strategies in Angular.

What is a change detection strategy?

A change detection strategy is a mechanism that decides when Angular should update the view of a component.

Angular offers the following change detection strategies:

  • ChangeDetectionStrategy.Default
  • ChangeDetectionStrategy.OnPush

Any newly generated component uses the default strategy.

ChangeDetectionStrategy.Default

Default strategy triggers change detection for DOM events, timers, promises, XHR, observables etc. Therefore, every time something changes in our application, a change detection will run on all components to update the view.

Angular achieves this by creating a change detector, which is responsible for listening to changes and updating the view of the component.

ChangeDetectionStrategy.OnPush

The OnPush strategy can be used to optimise the performance of an Angular application.

With OnPush strategy, Angular only performs a change detection cycle on a component and its children, if there is a change in the component’s input properties or if an event is triggered.

OnPush triggers change detection when:

  • DOM events listened by the component emit
  • async pipe receives a new event
  • @Input() property updates by change detection
  • Explicitly registration using ChangeDetectorRef::markForCheck

If the component’s data model is updated outside of Angular’s change detection mechanism, Angular will not update the view.

This may sound like a drawback, but it’s actually a useful feature when used correctly.

The OnPush strategy is designed to reduce the number of unnecessary change detection cycles. This is especially important in complex applications, where change detection can become a bottleneck and degrading overall performance of the application.

How to use onPush strategy

To use the OnPush strategy, you simply need to set the changeDetection property of the component’s @Component decorator to ChangeDetectionStrategy.OnPush.

Here’s an example:

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

@Component({
  selector: 'app-hello-world',
  template: `
    <p>{{ message }}</p>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class HelloWorldComponent {
  message = 'Hello, World!';
}
Code language: TypeScript (typescript)

It’s important to note that when using the OnPush strategy, you need to make sure that Angular is aware of any changes in the component’s data model.

This can be done by using the markForCheck method from the ChangeDetectorRef class.

import { ChangeDetectorRef } from '@angular/core';

constructor(private cd: ChangeDetectorRef) { }

ngOnInit() {
  // Do some work that changes data
  this.cd.markForCheck();
}
Code language: TypeScript (typescript)

The ChangeDetectorRef class provides a way to interact with the change detection mechanism and manually trigger change detection.

You can mark a component or its ancestors for checking using the markForCheck method. Next time change detection runs, the view will reflect the new state of data.

markForCheck() vs detectChanges()

Another method available on the change detector is detectChanges. You might be wondering, what is the difference between markForCheck and detectChanges.

The key difference between these is that detectChanges() triggers change detection, while markForCheck() doesn’t trigger change detection.

With detectChanges(), the change detection will run for the current component and all its children. This is a common fix to the infamous error Expression has changed after it was checked.

On the other hand, markForCheck() simply goes upwards from the current component to the root component and updates their view state to ChecksEnabled. The change detection for the component will happen in the future either as part of the current or next change detection cycle. The parent component views will also be checked even if they had detached change detectors. 

Should I use onPush strategy?

If change detection is a bottle-neck in your component, go ahead and try onPush change detection strategy. This is a great way to fine-tune the performance of your application.

However, premature optimisation can have an adverse effect on your application.

If you use onPush strategy without a valid reason, the component code will be more complex, harder to test and more difficult to maintain.

Based on my experience, the default strategy will work just fine for most small to medium sized applications.

Conclusion

In this post, we’ve looked at the different change detection strategies available in Angular. Default strategy automatically updates view when there is a change in the data model. OnPush strategy allows us to optimise performance by giving us the freedom to choose when to check for changes.

Umut Esen

Software Engineer specialising in full-stack web application development.

Leave a Reply