Monthly Archives: July 2017

Change detection in Angular (Obviously 2.0 & greater)

Let me define first what change detection is, in simple terms any state change that needs to be propagated to UI and ice versa. Now the application state changes can happen in   following ways which apparently all are asynchronous in nature.

  • Events – Click, selection changed, keyup
  • Remote API Ajax Calls(XHR)
  • Timers – setTimeout(), setInterval()

Now for sure we know what how the application state change happens but there is still work of notifying Angular that state has changed and do the further magic. After all in the angular code we are still using Native API’s (like setTimeOut…) there are no angular specific API’s. So there is some thing called as Zone.js which does all the magic. You must have seen zone references whenever u see any error in the console window.

So what are zone and how do they work?

As one liner definition we can say that Zones are basically an execution context for asynchronous operations. Zones basically monkey patch these native methods and provide hooks for executing change detection.  As soon as we embed zone.js in our site, pretty much all methods that cause asynchronous operations are monkey-patched to run in a new zone. For example, when we call setTimeout() we actually call Zone.setTimeout().

So now we know that zones have hooks for asynchronous operation but how does it marry with angular, answer lies in to the ApplicationRef class and source code below, it simplified version of the code….

class ApplicationRef {

  changeDetectorRefs:ChangeDetectorRef[] = [];

  constructor(private zone: NgZone) {
    this.zone.onTurnDone
      .subscribe(() => this.zone.run(() => this.tick());
  }

  tick() {
    this.changeDetectorRefs
      .forEach((ref) => ref.detectChanges());
  }
}

ApplicationRef listens to NgZones onTurnDone event. Whenever this event is fired, it executes a tick() function which essentially performs change detection.

In Angular, each component has its own change detector. So in the component tree each component will have its own change detector. This allows us to control, for each component individually, how and when change detection is performed

ChnageDetection1

Let us assume that in the component tree an event has been triggered in the Child 2.1 component.  As mentioned earlier zones have hook in to these event handlers, zones execute the given handler and notify Angular when it is done, which eventually causes Angular to perform change detection. Now for angular change detection always flows from top to bottom. Change detection is also always performed from top to bottom for every single component, every single time, starting from the root component

ChnageDetection2ChnageDetection3ChnageDetection4

This is much more predictable change detection than in Angular JS where it used to digest loop or cycle.

CD (change detection) gets stable after a one pass, if one of our components causes any additional side effects after the first run during change detection, then angular will throw an exception.

Now that we have talked @ Change detectors in Angular but what are these Change detectors, well these are not one fit all generic classes which does the change detection for all components. Angular creates CD classes at runtime for each component, which are monomorphic, because they know exactly what the shape of the component’s model is.  Monomorphic are king of opposite of polymorphic which do not have any runt time overrides or sub types.

Angular also gives control of the change detection, if we wish to. We can tell angular to run CD only for the part of the component hierarchy tree. This can be achieved with 2 things

  • Immutable data structures
  • Observables

Lets consider optimization through Immutability first, consider the followin component

import { Component, OnInit } from '@angular/core';
import { UtilityService } from './../services/utility.service';
@Component({
  templateUrl: ''
})
export class GrandChildComponent implements OnInit {
  public greatGrandChildDetails: GreatGrandChildDetails;
  public currentDateTime : string;
  constructor(private utilService: UtilityService) {
    this.currentDateTime = this.utilService.currentDateTime.toISOString();
    this.greatGrandChildDetails = new GreatGrandChildDetails();
    this.greatGrandChildDetails.firstName = "Amol";
    this.greatGrandChildDetails.lastName = "Gote";
   }

  ngOnInit() {
  }

  changeGrandChildData(){
    this.greatGrandChildDetails.firstName = "John";
    this.greatGrandChildDetails.lastName = "Doe";
  }

}

export class GreatGrandChildDetails{
  firstName: string;
  lastName: string;
}

GrandChildComponent uses as a child component, which has an input property details. We’re passing data to that component with GrandChildComponent own greatGrandChildDetails property. greatGrandChildDetails is an object with two properties. In addition, there’s a method changeGreatGrandChildData(), which changes the first name and last name of greatGrandChildDetails, No magic going on here.
The important part is that changeGreatGrandChildData() mutates greatGrandChildDetails, by changing its first and last name property. Even though that property is going to be changed, the greatGrandChildDetails reference itself stays the same.
What happens when change detection is performed, assuming that some event causes changeGreatGrandChildData() to be executed? First, greatGrandChildDetails.firstName and greatGrandChildDetails.lastName gets changed, and then it’s passed to . ’s change detector. It now checks if the given details (greatGrandChildDetails) is still the same as before, and yes, it is (reference hasn’t changed), however, the first and last name property has changed, so Angular will perform change detection for that object nonetheless. Because objects are mutable by default in JavaScript (except for primitives), Angular has to be conservative and run change detection every single time for every component when an event happens. This is where immutable data structures come into play.

Immutable objects cannot be changed and we have to change then we have change the reference.

this.greatGrandChildDetails = Immutable.create({
              firstName: 'Amol',
              lastName: 'Gote'
            });

this.greatGrandChildDetailsNew = this.greatGrandChildDetails
                                  .set('firstName', 'John');

In the above case Immutable.create is just sample API to create Immutable, you can use any library to create immutable objects. Now when I am changing the first Name property then it is going to return a whole new reference to the object.

this.greatGrandChildDetails !=== this.greatGrandChildDetailsNew from a reference perspective both are pointing to different references. If we use immutable objects in our Angular app, all we need to do is tell Angular that a component can skip change detection, if the input has not changed, let’s look at the great-grand-child component.

Immutable-Component

We can skip entire subtrees when immutable objects are used and Angular is informed accordingly.
Immutable
ChangeDetectionStrategy.OnPush – This will inform Angular that our component only depends on its inputs and that any object that is passed to it should be considered immutable.

Now let’s talk @ Observables

Observables give us some guarantees of when a change has happened, unlike immutable objects, they don’t give us new references, instead, they fire events we can subscribe to in order to react to them.

Lets look at this example

import { Component, OnInit, ChangeDetectionStrategy, Input } from '@angular/core';
import { UtilityService } from './../services/utility.service';
@Component({
  template: '{{hitCounter}}',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChildComponent implements OnInit {

  @Input() addChildItemStream:Observable;
  public hitCounter : number = 0;

  ngOnInit() {
    this.addChildItemStream.subscribe(() => {
      this.hitCounter++; 
    })
  }

}

We set the change detection strategy to OnPush, so change detection isn’t performed all the time, only when the component’s input properties change, the reference of addChildItemStream will never change, so change detection is never performed for this component’s subtree. This is a problem because the component subscribes to that stream in its ngOnInit life cycle hook and increments the counter. This is application state change and we want to have this reflected in our view. This is where ChangeDetectorRef comes to the rescue, it has an API markForCheck(), it marks the path from our component until root to be checked for the next change detection run, it simply iterates upwards and enables checks for every parent component up to the root.

import { Component, OnInit, ChangeDetectionStrategy, Input, ChangeDetectorRef } from '@angular/core';
import { UtilityService } from './../services/utility.service';
@Component({
  template: '{{hitCounter}}',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChildComponent implements OnInit {

  @Input() addChildItemStream:Observable<any>;
  public hitCounter : number = 0;
  constructor(private changeDetector: ChangeDetectorRef) {

  }

  ngOnInit() {
    this.addChildItemStream.subscribe(() => {
      this.hitCounter++; // application state changed
      this.changeDetector.markForCheck();
    })
  }

}
Advertisements

Typeaheads – Avoid Frequent Backend calls with RxJS debounce……

If you are implementing any typeaheads like when as user types in and you display the result immediately, obviously for displaying these results you are back end API calls from your front end angular app. Now there is one gotcha over typically the way you do it on key for some number of characters (for.e.g 3 characters) you start making back end api calls for each key press, which poses challenge in itself. So what if if we have an way we make an API call when user user stops typing in or with some time delays. This is where RxJS debounceTime comes to rescue.

As explained earlier  RxJS allows you to work with asynchronous data streams. Now once a value is emitted from stream, debounce will pause its emission for specific X amount of time to see if another value is emitted, it is blocking the stream during this time. If a new value is emitted during the debounce time then the timer is restarted and debounce waits again for the full time. If its timer expires without any new value being emitted, it let the latest value pass. This can be very helpful in the scenarios of type-ahead.

Here is the sample screen shot.

Debounce sample

So as I type in I making calls to to Git Hub User API and displaying the HTML URL. So over here I have stopped thrice so three results.

Here is the code snippet for component and Html Template

import { Component, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/fromEvent';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/pluck';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/map';
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements AfterViewInit  {
  gitUsers: string[];
  title = 'app works!';
  @ViewChild('searchInput') searchInput: ElementRef;

  constructor (private http: Http) {
    this.gitUsers = new Array<string>();
  }

  ngAfterViewInit(){
    console.log(this.searchInput.nativeElement.value);
    Observable.fromEvent(this.searchInput.nativeElement, 'keyup')
      .debounceTime(500)
      .pluck('target', 'value')
      .filter((value: string) => value.trim().length > 0)
      .map((values: string) => {
        return this.gitUsersSearch(values)
      })
      .forEach(data => {
          data.subscribe(
            (result: Response) => {
              console.log(result);
              let responseBody = result.json();    
              this.gitUsers.push(responseBody.html_url);
              console.log(this.gitUsers);
            }
          );
      });
  }
  gitUsersSearch(searchTerm: string){
    console.log("Inside gitUsersSearch");      
    return this.http.get("https://api.github.com/users/" + searchTerm);
  }
}

Html Template

Debounce sample2

So apart from debounceTime I have used following additional API’s from Observables

  • Pluck –  It returns an Observable containing the value of a specified nested property from all elements in the Observable sequence. If a property can’t be resolved, it will return undefined for that value.
  • filter –Filter operator filters an Observable by only allowing items through that pass a test that you specify in the form of a predicate function.