loading...

Change Detection Strategy in Angular

gaurangdhorda profile image GaurangDhorda Updated on ・2 min read

[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.

Alt Text

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

  1. 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.

  2. By using ChangeDetectorRef and injecting using dependency into the constructor of the component.This is angular way of doing changeDetection using detectChanges() method of ChangeDetectorRef. 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();  
          }
    

Discussion

markdown guide