Change Detection means updating the DOM whenever data is changed. Angular provides two strategies for Change Detection.
- Default strategy
- onPush strategy
In default strategy, whenever any data is mutated or changed, Angular will run change detector to update the DOM. In onPush strategy, Angular will only run change detector only when new reference is passed to @Input() data.
To update the DOM with updated data, Angular provides its own change detector to each component, which is responsible to detect change and update the DOM.
Let us say we have a MessageComponent as listed below:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Component, Input } from '@angular/core'; | |
@Component({ | |
selector: 'app-message', | |
template: ` | |
<h2> | |
Hey {{person.firstname}} {{person.lastname}} ! | |
</h2> | |
` | |
}) | |
export class MessageComponent { | |
@Input() person; | |
} |
In addition, we are using MessageComponent inside AppComponent as below,
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Component, OnInit } from '@angular/core'; | |
@Component({ | |
selector: 'app-root', | |
template: ` | |
<app-message [person]='p'></app-message> | |
<button (click)='changeName()'>Change Name</button> | |
` | |
}) | |
export class AppComponent implements OnInit { | |
p: any; | |
ngOnInit(): void { | |
this.p = { | |
firstname: 'Brad', | |
lastname: 'Cooper' | |
}; | |
} | |
} |
Let us talk through the code: all we are doing is using MessageComponent as child inside AppComponent and setting value of person using the property binding. At this point on running the application, you will get name printed as output.
Next, let us go ahead and update firstname property on the button click in AppComponent class below:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
changeName() { | |
this.p.firstname = 'Foo'; | |
} |
As soon as we changed the property of mutable object P, Angular fires the change detector to make sure that the DOM (or view) is in sync with the model (in this case object p). For each property changes, Angular change detector will traverse the component tree and update the DOM.
Let us start with understanding about component tree. An Angular application can be seen as a component tree. It starts with a root component and then go through to child components. In Angular, data flows from top to bottom in the component tree.
Whenever @Input type property will be changed, Angular change detector will start form the root component and traverse all child components to update the DOM. Any changes in primitive type’s property will cause Angular change detection to detect the change and update the DOM.
In above code snippet, you will find that on click of the button, the first name in the model will be changed. Then, change detection will be fired to traverse from root to bottom to update the view in MessageComponent.
There could be various reasons of Angular change detector to come into action and start traversing the component tree. They are:
- Events fired such as button click, etc.
- AJAX call or XHR requests
- Use of JavaScript timer functions such as setTimeOut , SetInterval
Now, as you see, a single property change can cause change detector to traverse through the whole component tree. Traversing and change detection is a heavy process, which may cause performance degradation of application. Imagine that there are thousands of components in the tree and mutation of any data property can cause change detector to traverse all thousand components to update the DOM. To avoid this, there could be scenario when you may want to instruct Angular that when change detector should run for a component and its subtree, you can instruct a component’s change detector to run only when object references changes instead of mutation of any property by choosing onPush ChangeDetection strategy.
You may wish to instruct Angular to run change detection on components and their sub-tree only when new references are passed to them versus when data is simply mutated by setting change detection strategy to onPush.
Let us go back to our example where we are passing object to MessageComponent. In the last example, we just changed firstname property and that causes change detector to run and to update the view of MessageComponent. However, now we want change detector to only run when reference of passed object is changed instead of just a property value. To do that let us modify MessageComponent to use OnPush ChangeDetection strategy. To do this set changeDetectionproperty of @Component decorator to ChangeDetectionStrategy.OnPush as shown in listing below:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Component, Input, ChangeDetectionStrategy } from '@angular/core'; | |
@Component({ | |
selector: 'app-message', | |
changeDetection: ChangeDetectionStrategy.OnPush, | |
template: ` | |
<h2> | |
Hey {{person.firstname}} {{person.lastname}} ! | |
</h2> | |
` | |
}) | |
export class MessageComponent { | |
@Input() person; | |
} |
At this point when you run the application, on click event of the button in AppComponent change detector will not run for MessageComponent, as only a property is being changed and reference is not changing. Since change detection strategy is set to onPush, now change detector will only run when reference of @Input property is changed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
changeName() { | |
this.p = { | |
firstname: 'Foo', | |
lastname: 'Kooper' | |
}; | |
} |
In the above code snippet, we are changing reference of object instead of just mutating just one property. Now when you run application, you will find on the click of the button that the DOM is being updated with new value.
By using onPush Change Detection, Angular will only check the tree if the reference passed to the component is changed instead of some property changed in the object. We can summarize that, and Use Immutable Object with onPush Change Detection to improve performance and run the change detector for component tree when object reference is changed.
We can further improve performance by using RxJS Observables because they emit new values without changing the reference of the object. We can subscribe to the observable for new value and then manually run ChangeDetector.
Leave a Reply