Change Detection Strategy in Angular
GaurangDhorda
Posted on February 6, 2020
[Update] https://netbasal.com/reduce-change-detection-cycles-with-event-coalescing-in-angular-c4037199859f
Angular9 is released, and we have now even more improved change detection cycle with option setting in main.ts file
platformBrowserDynamic()
.bootstrapModule(AppModule, { ngZoneEventCoalescing: true })
.catch(err => console.error(err));
In angular framework we need to detect changes whenever there is changes on model side of your component happen. This has to reflect to update component view [DOM-UPDATE] automatically. So doing this work angular provides us zone.js, and handles every angular change detection cycle out of the box.
Angular runs change detection for every component try every time model has changed. This would cause more performance issue when you have plenty number of component tree in your angular app and you only update model of one component of tree, by default angular runs change detection for all of the available component tree and this is big performance issue. [ Later on we see how to update this default strategy ].
One major common error behavior you as a developer see while changing component model to frequently is "ExpresionChangedAfterItHasBeenCheckedError". So, What does this error tells us is simply that, angular is in ChangeDetection cycle and after just in between some model values are changed and its quite confusing to know which value to update to view. [There are ways to fix this problem too].
Angular uses zone.js and takes all component reference as a tree data structures. So, theres must to have one parenet component and others are their child component.
In below code example you can get "ExpresionHasChangedAfterItHasBeenCheckedError". Because of first We are initializing checkboxes
object and in ngAfterViewInit()
life cycle hook we attempt to change objects check property to false. Thus angular gives error because of ChangeDetection has found that something has changed before finishing updating DOM cycle and that is ambiguity. Simply, This error is coming because existing value is getting updated immediately after getting initialized.
export class AppComponent {
name = 'Angular';
checkboxes = [{
label : 'Sunday',
checked : true
},{
label : 'Monday',
checked : false
},{
label : 'Tuesday',
checked : false
},{
label : 'Wednesday',
checked : true
},{
label : 'Thursday',
checked : false
},{
label : 'Friday',
checked : true
},{
label : 'Saturday',
checked : true
}];
ngOnInit(){
}
ngAfterViewInit(){
this.changeStatus() // This line cause ExpresionChangedError
}
changeStatus(){
this.checkboxes.map(item => item.checked === false ? item.checked=true : item.checked=false);
}
}
}
Solutions to Above Problem
-
Very First and dirty and quick solution is to use
setTimeOut()
async method.Change this code in
ngAfterViewInit()
to this...
ngAfterViewInit(){ setTimeout(()=> { this.changeStatus() }) }
By applying above changes
setTimeOut()
will call angular change detection cycle for us automatically. -
By using
ChangeDetectorRef
and injecting using dependency into the constructor of the component.This is angular way of doing changeDetection usingdetectChanges()
method ofChangeDetectorRef
. we tell angular explicitly to detect change of DOM because of We have changed model.
constructor(private cdr : ChangeDetectorRef){} ngAfterViewInit(){ this.changeStatus(); this.cdr.detectChanges(); }
Posted on February 6, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.