Change detection strategy in Angular
Diario del capitán, fecha estelar d309.y38/AB
After having worked in dozens of Angular projects as a developer, I have seen multiple occasions in which the default configuration of Angular is not always the most recommendable.
Today, I am going to talk about the detection strategy in Angular and how to change it to improve performance and to adapt it to your project requirements.
By default, Angular components use ChangeDetectionStrategy.Default strategy. This means that every time something changes in your application, Angular will check every property of the component to verify if the view is up-to-date.
This is very useful because we don't have to worry about components not displaying its latest state in the view when a property changes, even if the change is deep inside an object.
The downside of ChangeDetectionStrategy.Default is its performance. When the application grows, Angular will need to check more and more components and their properties, consequently impacting the user experience.
To ease this potential problem we can change the strategy for each component:
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
selector: 'my-component',
templateUrl: './my.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MyComponent {
...
}
The above code sets the change detection strategy for a given component to OnPush.
The OnPush strategy will trigger the Change Detector only when:
- An Input reference changes. (I said reference, very important)
- If a component event handler gets triggered.
- When an observable emits a new value and we are using the pipe async in the template to observe.
If you need to run change detector once inside a component with ChangeDetectionStrategy.OnPush you can do it manually injecting the ChangeDetectorService and markForCheck method:
import { Component, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
@Component({
selector: 'my-component',
templateUrl: './my.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MyComponent {
constructor(private changeDetectorRef: ChangeDetectorRef){}
performSomething() {
// change the state of the component
....
this.changeDetectorRef.markForCheck();
}
}
Sometimes, this is not enough and you want to take control of the Change Detector. Performance-wise - it is the best option -, you can detach the Change Detector completely to reattach it or to call the Change Detector manually later.
import { Component, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
@Component({
selector: 'my-component',
templateUrl: './my.component.html'
})
export class MyComponent {
constructor(private changeDetectorRef: ChangeDetectorRef){
// detattach change detector for this component, any change will be ignored
this.changeDetectorRef.detach();
}
performSomething() {
// change the state of the component
....
this.changeDetectorRef.detectChanges();
}
enableDetector() {
this.changeDetectorRef.reattach();
}
}
Further reading
If you're interested in learning more about this configuration tweak, I'd strongly recommend reading the following sources:
- @angular/core/ChangeDetectionStrategy
- @angular/core/ChangeDetectorRef
- When does ChangeDetectionStrategy.OnPush Actually Run Change Detection?
- What's the difference between markForCheck() and detectChanges()
- Everything you need to know about change detection in Angular
Thanks for reading so far! Hope you found this useful!