A Primer on Angular Change Detection

Maintaining a consistent and synchronized relationship between the application view and the data model is essential. This blog post will explore the inner workings of Change Detection in Angular, shedding light on its operational mechanisms, the various processes it entails, and strategies for optimizing it to enhance the performance of your application.

Change Detection is a mechanism that Angular uses to track changes in the application's data and synchronize these changes with the application view. This process ensures that the UI is always in sync with the underlying data model, providing a seamless user experience.

The Two-Step Process of Change Detection

Change Detection in Angular can be broken down into two main processes:

  1. View Checking: This is the first step in the process, where Angular synchronizes the component view with the data model. It involves checking the view for any changes that have occurred in the data and updating the view accordingly.

  2. Re-run of View Checking: After the initial view checking, Angular provides the capability to automatically re-run this process. This re-run ensures that any changes that might have been missed or any new changes that have occurred since the first run are accounted for and synchronized.

Now let us see how exactly it works in Angular.

// Note: This is an incomplete Angular component,
// I have just included the necessary parts
@Component({
    template: `
        <span> {{ count }} </span>
        <button (click)="handleClick()"> Increment </button>
    `
})
export class AppComponent() {
    count: number = 0;
    handleClick() {
        this.count++;
    }
}

Let us take the above example of a counter. If we run the above example, and click on the button, we can notice that the count value has increment. But how did the new value of count got reflected in the view? How was Angular able to sense that some application has changed?

Zone.js

Any event can cause changes in the state. These events range from user interactions like clicks, typing, and scrolling, to programmatic events such as HTTP responses. The crucial part is reflecting these state changes in the UI effectively. Angular employs the zone.js library to achieve this. Zone.js monkey patches various browser APIs, which allows Angular to be notified whenever certain asynchronous tasks are completed.

When such tasks - like setTimeout, promises, and UI events - complete, zone.js signals Angular that a change might have occurred. Angular then initiates a change detection cycle. This cycle starts at the root component and checks each and every component in the tree. The bindings are recalculated to reflect the latest value of state in the UI.

It is important to note that although Zonejs tell Angular, that some event happened and based on that application state may have changed, it is incapable of telling Angular exactly in which component that state may have changed, as a result Angular has to perform the view checking process for the entire tree, even if one component is affected by the event.

This causes a lot of overhead and performance bottle-necks. To counter this, we have something called as OnPush Change Detection Strategy. The above one which we saw, is called as Default Change Detection Strategy.

OnPush Change Detection Strategy

The OnPush Change Detection strategy in Angular introduces a more efficient approach to monitoring component states, through a mechanism known as "dirty checking."

In Angular's default strategy, the framework systematically traverses each component in the hierarchy. However, with the 'OnPush' change detection strategy, Angular specifically targets components identified as "dirty." This 'OnPush' strategy is set at the component level, allowing for a blend of both default and 'OnPush' strategies within the same application. 'OnPush' can be implemented gradually.

How it works?

When an event prompts Zone.js to initiate the view checking process, it starts with the root component (A) and then recursively progresses to its children. Upon reaching component B, Angular recognizes its configuration with the 'OnPush' Change Detection Strategy. It checks if component B is marked as "dirty." If it is, Angular proceeds to check component B and its descendants; if not, it skips them. Thus saving computations.

A component is marked as "dirty" in several ways, either automatically by Angular or manually by the developer. Angular automatically marks a component as dirty in the following scenarios:

  1. An event originates from the component or any of its children.

  2. There is a change in the inputs of the component.

In situations outside these scenarios, developers can explicitly mark a component as dirty using changeDetectorRef.

In conclusion, while there is much to learn and apply in Angular's 'OnPush' change detection strategy, its thoughtful utilization can contribute positively to application performance. As developers, our ongoing exploration of Angular's automatic and manual mechanisms for managing view updates helps us to incrementally enhance the efficiency and responsiveness of our applications.

Looking ahead, the next blog post will delve into the realm of signals and zoneless change detection, further expanding our understanding and capabilities in crafting sophisticated Angular solutions. Stay tuned for more insights and deep dives into these advanced topics.