Angular2 Change

Detection

Ahsan Ayaz

Senior Software Engineer

Recurship

What Is Change Detection?

Change detection is a mechanism which detects the ... changes 

No Seriously !

What is Change Detection ??

View

Model

Model Changes

View Changes

Updates View

Updates Model

Change Detection

<profile-dropdown>

     <h3>Hi {{user.firstname}} {{user.lastname}}</h3>

     <button (click)="goToProfile()">
          Profile
     </button>
​</profile-dropdown>

Profile Dropdown Template

export class ProfileDropdownComponent{
      public user = {"firstname" : "", "lastname" : ""}                                                 constructor(private myApi: MyAPI){

           this.myApi.getUser().subscribe(user => {

                   this.user =  user;    

           });

      }

}

Profile Dropdown Component

How does Angular2 handle Change Detection ??

Well Nope !

Angular Handles it using Zones.

Zones what?

Angular uses ZoneJS

Zone.js provides a mechanism, called zones, for encapsulating and intercepting asynchronous activities in the browser (e.g. setTimeout, , promises).

Provides a global zone which can be extended & forked.

NgZone

Angular extends the global zone of Zone.js and adds its own behaviours using NgZone Service.

Well ZoneJS seems amazing. Is it my best buddy now?

ZoneJS uses Monkey Patching

ZoneJS uses Monkey Patching to intercept the events such as setTimeout, addEventListener, HTMLElement.prototype.click

These interceptions can sometimes be painful or have unexpected behaviours. Especially while working with third party libraries.

AngularJS and ZoneJS

What notifies ZoneJS & changes application state:

  1. setTimeout, setInterval

  2. XHR calls

  3. click, mousemove, mousedown, submit etc

Sometimes you would like to run things outside of Angular. Well NgZone provides a method named runOutsideAngular.

Running things outside of NgZone (ZoneJS)

The Angular2 application subscribes to ZoneJS's onTurnDone event

Sneak Peak at the Source Code

We can see that inside the subscription handler, it is calling { this._zone.run(() => { this.tick(); });

If we dig into the tick method, you'll see that it iterates through the changeDetectors and calls the detectChanges() method for each of them.

Change Detection

Angular1

10 $digest() iterations reached. Aborting!

 

Angular2

Change detection tree

Change detection flow in Angular2

event fired

Change detection insights

  • The flow is always from root component to deep nodes
  • There is no global detection cycle. That is every component handles its own change detection
  • It is faster because of monomorphism
  • We can use different strategies of change detection for each each component individually

Is Angular2 Change Detection Smart?

Angular's detection is smart but it has to check every model because some change might have happened to the models. 

<h4>Hey {{user.firstname}} {{user.lastname}}
export class MyComponent{
    userArticles:Array<ArticleInterface> = [];
    user:UserInterface = {
        "firstname": "", 
        "lastname" : ""
    };

    constructor(){}

    updateUserInfo(){
        this.user.firstname = "Ahsan";
        this.user.lastname = "Ayaz";
    }

}

Can we control the detection? 

We can make the detection even more smart by using:

  1. Observables
  2. Immutable Objects
  3. ChangeDetectorRef

Immutable Objects

<div class="box-inner box-outside-zone" 
    [style.display]="isRunningOutsideAngular? 'block': 'none'" 
    #boxInner>
    <mini-box [coords]="coordinates"></mini-box>
</div>
export class ZoneDemoComponent implements OnInit{
  coordinates: CoordsInterface = null;

  ngOnInit() {
    this.coordinates = {
      position_x: 0,
      position_y: 0
    }
  }
  updateCoordinates($event){
    let position = {
      position_x: $event.offsetX? $event.offsetX + 2 : 0,
      position_y: $event.offsetY? $event.offsetY + 2 : 0
    };
    this.coordinates = position; 
    console.log(this.coordinates);
  }

ZoneDemoComponent

MiniBoxComponent

coordinates = {
     position_x: 0,
     position_y: 0
}
    <mini-box [coords]="coordinates">
    </mini-box>
updateCoordinates();

change detection occurs

user = Immutable.Map({firstname: "Ahsan", lastname:"Awan"});
editedUser:any;
myMethod(){
    this.editedUser = this.user.set('lastname', "Ayaz");        //returns a new object
    this.user.get('lastname'); // "Awan"
    this.editedUser.get('lastname'); // "Ayaz"
}

Immutable Objects Using Immutable.js

Angular won't handle the detection because the model  is immutable and always returns a new reference on update/change

Using ChangeDetectionStrategy to optimize our components for less change detection checks

@Component({
  selector: 'mini-box',
  templateUrl: './mini-box.component.html',
  styleUrls: ['./mini-box.component.css'],
  changeDetection: ChangeDetectionStrategy.OnPush // *** MAGIC ***
})
export class MiniBoxComponent implements OnInit {
  @Input("coords") coordinates;
  constructor() { }

  ngOnInit() {
    
  }
}

// Perform change detection ONLY when the "coords" input changes

ZoneDemoComponent

MiniBoxComponent

coordinates = {
     position_x: 0,
     position_y: 0
}
    <mini-box [coords]="coordinates">
    </mini-box>

change detection occurs

since coordinates didn't change,

skips change detection

Using ChangeDetectorRef

We can use Angular's ChangeDetectorRef to attach or detach the change detector from the component on demand.

 

import { Component, ChangeDetectorRef } from '@angular/core';

export class AppComponent {
  constructor (private cdRef : ChangeDetectorRef){}
  cdDetached: boolean = false;


  toggeDetachCd(){
    this.cdDetached = !this.cdDetached;
    if(this.cdDetached){
      this.cdRef.detach();
    }
    else{
      this.cdRef.reattach();
    }
  }
}

Questions ??

Angular2 Change

Detection

Ahsan Ayaz

Senior Software Engineer

Recurship

Thank You

ng2-change-detection

By Ahsan Ayaz

ng2-change-detection

Angular2 Change Detection, ZoneJS, Immutable Objects, Angular2 Change Detector Ref

  • 881
Loading comments...

More from Ahsan Ayaz