top

Exploring Change Detection Strategies In Angular

IntroductionOne of the core features of JavaScript frameworks, particularly Angular is the ability to detect data changes within any component and speedily re-renders the view automatically to reflect that change.This amongst other things makes the process of representing the current state of the DOM somewhat easy. Generally, an understanding of tools like Angular will be more profound with a full grasp of how most things work under the hood.This Guide aims to give you a solid foundation on how change detection works in Angular. With this, you can further explore the subject and gain more insight.PrerequisiteA basic knowledge of JavaScript and Angular will help you get a profound understanding of this tutorial. This will not be an introduction to Angular but a quick look at how change detection works. If you are ready, let’s dive right into it! What is change detection?Managing the synchronization of app state and the user interface has long been a major source of complexity in UI development. The state of an application like forms, links, and buttons varies from a data structure like objects to arrays and so on. Based on a particular event to manipulate the user interface from the browser, change detector will generate a DOM output and renders it to the user.Change detection monitors the state of data within the browser especially at runtime when DOM has already been rendered. Let’s see how Angular detects and handles this.What causes a change detection in AngularTechnically speaking, change detection becomes more challenging when data keeps changing over time as a result of users’ interaction with the user interface of a web application.Angular as a web application framework uses plain old JavaScript objects ( POJO ) for models. Unfortunately, these models don’t have set methods that can be called to update them. Some frameworks have an API to provide the functionality of updating the DOM once a change has been detected. As this isn’t available in Angular, it has to run a change detection at a specific point where the model state needs to be changed.Model state in Angular can change as a result of, but not limited to:DOM events ( click, focus, submit, blur and so on )AJAX requestsTimers ( setTimeout(), setInterval() )So, in order to keep the DOM updated, Angular needs to perform its change detection right after any of this entry point.How Angular detects a change Every Angular application is a tree of components. The image below illustrates an example of an Angular application with few components: At runtime, Angular creates a change detection class (CD) for every component in an application. So, just like we have a tree of components, we also have a tree of change detectors: Each change detector is created based on the data structure of the components that it targets. In a nutshell, when change detection runs, Angular walks down the tree of change detector that takes care of every individual component.Demonstrating a strategy like change detection is best done by using an example, some sort of demo. So, let’s quickly set up a simple Angular project. Note: I assumed that you already have Angular-cli tool installed. Check here for instructions if otherwise.Run the command below to create an Angular application named book-store## create a new angular project ng new book-store Generate components for the bookstore:## Generate books component ng g c books ## Generate book component ng g c book In all the components, we are going to import and implement the DoCheck interface. This is a lifecycle hook that is called whenever Angular checks all the components for any change to the state of models. We won’t do much but to simply log each component’s specific information to the console. To get started, add the content below to the appComponent:// ./src/app/app.component.tsimport { Component, DoCheck } from '@angular/core';   @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] })export class AppComponent implements DoCheck { title: 'Change Detection Angular' books = [   { title: 'b1'},   { title: 'b2'},   { title: 'b3'} ];   ngDoCheck() {   console.log("AppComponent is being checked!"); }   onClick() {} } And update the appComponent template with:// ./src/app/app.component.html <div style="text-align:center"> <h1>    {{ title }} </h1> </div>   <button (click)="onClick()"> Click the button in AppComponent </button>   <app-books [books]="books"></app-books>bookComponentSimilarly for the bookComponent, locate ./src/app/book/book.component.tsand paste the code below:// ./src/app/book/book.component.tsimport { Component, Input, DoCheck } from '@angular/core';   @Component({ selector: 'app-book', templateUrl: './book.component.html', styleUrls: ['./book.component.css'] })export class BookComponent implements DoCheck { @Input() book;   ngDoCheck() {   console.log("Book Component is being checked!"); } } booksComponentThe booksComponent :// ./src/app/books/books.component.tsimport { Component, Input, DoCheck } from '@angular/core';   @Component({ selector: 'app-books', templateUrl: './books.component.html', styleUrls: ['./books.component.css'] })export class BooksComponent implements DoCheck {   @Input() books;   ngDoCheck() {   console.log("Books Component is being checked!"); }   } The booksComponent template: // ./src/app/books/books.component.html <h1> List of Books </h1>   <app-book *ngFor="let b of books" [book]="b"></app-book> A careful look at all the components, you will realise that we have successfully implemented this DoCheck interface at three different level i.e appComponent, bookComponent and booksComponent.Change detection processMaking use of the newly created application, let’s examine how the change detection process is being carried out. Under the hood, Angular internally makes use of a library called zone.js to detect whenever a change occurs in an application. You can read more about the library here.Next, run the application with:ng serve Click on the button to see the results on the console.So what's going on here?This shows that whenever the button is clicked, whether there is a change in our application state or not, Angular traverses the tree of change detector and kind of asks the change detector for each component if there is a change in that component or not. This operation runs from top to bottom throughout the application irrespective of the event’s source location and it applies to all DOM event in an Angular application.Similarly, a setTimeout() function will result in the same process. To try this out, update the appComponent by adding a constructor:// ./src/app/app.component.tsimport { Component, DoCheck } from '@angular/core';   ...export class AppComponent implements DoCheck { ... // add a constructor constructor() {   setTimeout(() => {}, 3000); }   ... } Change detection strategiesWe have two change detection strategies to understand in Angular:Default strategyonPushThe default strategy looks at all the binding expressions in the component’s template that needs to be updated and keeps track of its previous as well as the new state. Once there is a change, it notifies Angular and that’s when the components need to be re-rendered. Let's’ take a look at an example, in the appComponent, go ahead and attach a function to the onClick event and change the title of the first book in our store like this:// ./src/app/app.component.ts import { Component, DoCheck } from '@angular/core';   @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements DoCheck { ... // change the title of the first book onClick() {   this.books[0].title = "My new title" } } So, whenever we clicked the button, you would notice that the title of the first book in our list is updated. This is because the change detector for the bookComponent looks at the binding expression (i.e {{ book.title }}) in the template and was able to re-render the new state of the element.PerformanceChange detection is very performant, but once the application starts to grow with more complexity and lots of components with complex views and large dataset, the default strategy can be a little bit slow during this process because change detection will have to perform more and more work.Imagine having up to 10 or more binding expressions within a component that needs to be updated. This can amount up to a thousand or more comparison checks carried out simultaneously within a component every time we have a DOM manipulating browser event. Though change detectors are highly optimized to perform thousands of checks in just a few milliseconds.So, this large comparison checks may have a noticeable impact on your application. This is where the second change detection strategy ( i.e onPush) can help to optimize the change detection process by reducing the number of comparison checks.A quick example:Go back to the bookComponent and import the changeDetection strategy as shown below:// ./src/app/book/book.component.ts import { Component, Input, DoCheck, ChangeDetectionStrategy } from '@angular/core';   @Component({ selector: 'app-book', templateUrl: './book.component.html', styleUrls: ['./book.component.css'], changeDetection: ChangeDetectionStrategy.OnPush // add this }) export class BookComponent implements DoCheck { @Input() book;   ngDoCheck() {   console.log("Book Component is being checked!"); } } This means that each component can have its own change detection strategy, now go back to the browser and click the button again.Have you noticed that when we clicked the button, the title of the first book did not change? And that is because we are updating the title property of the same object.The book object is the same before and after the click handler was executed. To get this right, whenever we use onPush strategy, rather than modifying the object, we should create a new copy of it with the updated property. In other words, we should treat the objects as immutable just like a value type in JavaScript.So, let’s replace the first book in this array with a new book object. Open the appComponent and update as shown:// ./src/app/app.component.ts import { Component, DoCheck } from '@angular/core';   @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements DoCheck { ... // create a new object onClick() {   this.books[0] = { title: "My new title" } } }Here, we created a new object and updated the title of the first book with it. This is applicable for objects with more than one property. Now go back to your browser and try it out again:ConclusionKnowledge acquired here will not only help you understand how change detection works in Angular but also help to improve the performance of your Angular application. Complete source code can be found on GitHub.I hope you found this helpful. Feel free to leave a comment if you have suggestions or any other questions.
Rated 4.5/5 based on 11 customer reviews
Normal Mode Dark Mode

Exploring Change Detection Strategies In Angular

Olususi Kayode Oluyemi
Blog
25th Oct, 2018
 Exploring Change Detection Strategies In Angular

Introduction

One of the core features of JavaScript frameworks, particularly Angular is the ability to detect data changes within any component and speedily re-renders the view automatically to reflect that change.

This amongst other things makes the process of representing the current state of the DOM somewhat easy. Generally, an understanding of tools like Angular will be more profound with a full grasp of how most things work under the hood.

This Guide aims to give you a solid foundation on how change detection works in Angular. With this, you can further explore the subject and gain more insight.

Prerequisite

A basic knowledge of JavaScript and Angular will help you get a profound understanding of this tutorial. This will not be an introduction to Angular but a quick look at how change detection works. If you are ready, let’s dive right into it! 

What is change detection?

Managing the synchronization of app state and the user interface has long been a major source of complexity in UI development. The state of an application like forms, links, and buttons varies from a data structure like objects to arrays and so on. Based on a particular event to manipulate the user interface from the browser, change detector will generate a DOM output and renders it to the user.

Change detection monitors the state of data within the browser especially at runtime when DOM has already been rendered. Let’s see how Angular detects and handles this.

What causes a change detection in Angular

Technically speaking, change detection becomes more challenging when data keeps changing over time as a result of users’ interaction with the user interface of a web application.

Angular as a web application framework uses plain old JavaScript objects ( POJO ) for models. Unfortunately, these models don’t have set methods that can be called to update them. Some frameworks have an API to provide the functionality of updating the DOM once a change has been detected. As this isn’t available in Angular, it has to run a change detection at a specific point where the model state needs to be changed.

Model state in Angular can change as a result of, but not limited to:

  1. DOM events ( click, focus, submit, blur and so on )
  2. AJAX requests
  3. Timers ( setTimeout(), setInterval() )

So, in order to keep the DOM updated, Angular needs to perform its change detection right after any of this entry point.

How Angular detects a change 

Every Angular application is a tree of components. The image below illustrates an example of an Angular application with few components:

 Angular detects a change

At runtime, Angular creates a change detection class (CD) for every component in an application. So, just like we have a tree of components, we also have a tree of change detectors: 

Angular detects a change

Each change detector is created based on the data structure of the components that it targets. In a nutshell, when change detection runs, Angular walks down the tree of change detector that takes care of every individual component.

Demonstrating a strategy like change detection is best done by using an example, some sort of demo. So, let’s quickly set up a simple Angular project.

 Note: I assumed that you already have Angular-cli tool installed. Check here for instructions if otherwise.

Run the command below to create an Angular application named book-store

## create a new angular project
ng new book-store

Generate components for the bookstore:

## Generate books component
ng g c books
## Generate book component
ng g c book

In all the components, we are going to import and implement the DoCheck interface. This is a lifecycle hook that is called whenever Angular checks all the components for any change to the state of models. We won’t do much but to simply log each component’s specific information to the console. To get started, add the content below to the appComponent:

// ./src/app/app.component.ts
import { Component, DoCheck } from '@angular/core';
 
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements DoCheck {
title: 'Change Detection Angular'
books = [
  { title: 'b1'},
  { title: 'b2'},
  { title: 'b3'}
];
 
ngDoCheck() {
  console.log("AppComponent is being checked!");
}
 
onClick() {}
}

And update the appComponent template with:

// ./src/app/app.component.html
<div style="text-align:center">
<h1>
   {{ title }}
</h1>
</div>
 
<button (click)="onClick()"> Click the button in AppComponent </button>
 
<app-books [books]="books"></app-books>


bookComponent

Similarly for the bookComponent, locate ./src/app/book/book.component.tsand paste the code below:

// ./src/app/book/book.component.ts
import { Component, Input, DoCheck } from '@angular/core';
 
@Component({
selector: 'app-book',
templateUrl: './book.component.html',
styleUrls: ['./book.component.css']
})
export class BookComponent implements DoCheck {
@Input() book;
 
ngDoCheck() {
  console.log("Book Component is being checked!");
}
}

booksComponent

The booksComponent :

// ./src/app/books/books.component.ts
import { Component, Input, DoCheck } from '@angular/core';
 
@Component({
selector: 'app-books',
templateUrl: './books.component.html',
styleUrls: ['./books.component.css']
})
export class BooksComponent implements DoCheck {
 
@Input() books;
 
ngDoCheck() {
  console.log("Books Component is being checked!");
}
 
}
The booksComponent template:
// ./src/app/books/books.component.html
<h1> List of Books </h1>
 
<app-book *ngFor="let b of books" [book]="b"></app-book>

A careful look at all the components, you will realise that we have successfully implemented this DoCheck interface at three different level i.e appComponent, bookComponent and booksComponent.

Change detection process

Making use of the newly created application, let’s examine how the change detection process is being carried out. Under the hood, Angular internally makes use of a library called zone.js to detect whenever a change occurs in an application. You can read more about the library here.

Next, run the application with:

ng serve

Change detection process

Click on the button to see the results on the console.

Change detection process

So what's going on here?

This shows that whenever the button is clicked, whether there is a change in our application state or not, Angular traverses the tree of change detector and kind of asks the change detector for each component if there is a change in that component or not. This operation runs from top to bottom throughout the application irrespective of the event’s source location and it applies to all DOM event in an Angular application.

Similarly, a setTimeout() function will result in the same process. To try this out, update the appComponent by adding a constructor:

// ./src/app/app.component.ts
import { Component, DoCheck } from '@angular/core';
 
...
export class AppComponent implements DoCheck {
...
// add a constructor
constructor() {
  setTimeout(() => {}, 3000);
}
 
...
}


Change detection strategies

We have two change detection strategies to understand in Angular:

  1. Default strategy
  2. onPush

The default strategy looks at all the binding expressions in the component’s template that needs to be updated and keeps track of its previous as well as the new state. Once there is a change, it notifies Angular and that’s when the components need to be re-rendered. Let's’ take a look at an example, in the appComponent, go ahead and attach a function to the onClick event and change the title of the first book in our store like this:

// ./src/app/app.component.ts
import { Component, DoCheck } from '@angular/core';
 
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements DoCheck {
...
// change the title of the first book
onClick() {
  this.books[0].title = "My new title"
}
}

So, whenever we clicked the button, you would notice that the title of the first book in our list is updated. This is because the change detector for the bookComponent looks at the binding expression (i.e {{ book.title }}) in the template and was able to re-render the new state of the element.

Change detection process


Performance

Change detection is very performant, but once the application starts to grow with more complexity and lots of components with complex views and large dataset, the default strategy can be a little bit slow during this process because change detection will have to perform more and more work.

Imagine having up to 10 or more binding expressions within a component that needs to be updated. This can amount up to a thousand or more comparison checks carried out simultaneously within a component every time we have a DOM manipulating browser event. Though change detectors are highly optimized to perform thousands of checks in just a few milliseconds.

So, this large comparison checks may have a noticeable impact on your application. This is where the second change detection strategy ( i.e onPush) can help to optimize the change detection process by reducing the number of comparison checks.

A quick example:

Go back to the bookComponent and import the changeDetection strategy as shown below:

// ./src/app/book/book.component.ts
import { Component, Input, DoCheck, ChangeDetectionStrategy } from '@angular/core';
 
@Component({
selector: 'app-book',
templateUrl: './book.component.html',
styleUrls: ['./book.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush // add this
})
export class BookComponent implements DoCheck {
@Input() book;
 
ngDoCheck() {
  console.log("Book Component is being checked!");
}
}

This means that each component can have its own change detection strategy, now go back to the browser and click the button again.

Change detection process

Have you noticed that when we clicked the button, the title of the first book did not change? And that is because we are updating the title property of the same object.

The book object is the same before and after the click handler was executed. To get this right, whenever we use onPush strategy, rather than modifying the object, we should create a new copy of it with the updated property. In other words, we should treat the objects as immutable just like a value type in JavaScript.

So, let’s replace the first book in this array with a new book object. Open the appComponent and update as shown:

// ./src/app/app.component.ts
import { Component, DoCheck } from '@angular/core';
 
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements DoCheck {
...
// create a new object
onClick() {
  this.books[0] = { title: "My new title" }
}
}

Here, we created a new object and updated the title of the first book with it. This is applicable for objects with more than one property. Now go back to your browser and try it out again:

Change detection process


Conclusion

Knowledge acquired here will not only help you understand how change detection works in Angular but also help to improve the performance of your Angular application. Complete source code can be found on GitHub.

I hope you found this helpful. Feel free to leave a comment if you have suggestions or any other questions.

Olususi

Olususi Kayode Oluyemi

Blog Author

A tech enthusiast, programming freak and a web development junkie who loves to embrace new technology.

Leave a Reply

Your email address will not be published. Required fields are marked *

Top comments

Vikas

November, 16th at 3:58pm
been looking for this everywhere. What a godsend. THANK YOU!

SUBSCRIBE OUR BLOG

Follow Us On

Share on