In my post on “Sharing data between angular components” we saw how @Input() decorator is used to pass data from parent to child components. In this post we will discuss Angular’s Change Detection Strategy and how it affects change detection on input property binding.

Before we start I would like to introduce concept of immutable and mutable data types. In simple terms, mutable means “subject to change or alteration” and immutable means “unalterable”. String, Number and Boolean are some of native JavaScript data types that are immutable. Example of mutable data types in JavaScript include Objects, Arrays.

In this example we will create a simple parent and child component association to explore the two Change Detection Strategies that can be used in Angular. At parent component we will use property binding to pass data to the child component using @Input decorator.

For the sake of simplicity I’ve removed most of the unrelated code that’s needed for this example to run. You can download complete code for this example from my GitHub.

my-parent.component.html

<div class="bg-light p-5"&gt;
    <app-my-child [message]="parentMessage" [messageObj]="messageObj"   [messageList]="messageList"&gt;</app-my-child&gt;
</div&gt;

We have three properties immutable String and mutable Object and Array properties that are passed to child component using property binding in view template.

my-parent.component.ts

export class MyParentComponent implements OnInit {
  parentMessage: string = "Parent Says Hello!!!!";  
  messageObj: Message;  
  messageList: any = ["Hello!!", "Hey!!"];

At child component we have three properties with are adorned with @Input decorators, that are then bound to the child’s view template to render.

my-child.component.ts

export class MyChildComponent implements OnInit {
  @Input() message:string; 
  @Input() messageObj:Message;
  @Input() messageList:any;

At our parent component we use button’s click event to update the binding properties using handler functions for each of these.

my-parent.component.html

<div&gt;
    <button type="button" class="btn btn-outline-primary mr-3" (click)="updateMessageObject()"&gt;Update Object</button&gt;
    <button type="button" class="btn btn-outline-info mr-3" (click)="updateMessageArray()"&gt;Update Array</button&gt;
    <button type="button" class="btn btn-outline-secondary" (click)="updateMessageString()"&gt;Update String</button&gt;
</div&gt;

my-parent.component.ts

 updateMessageObject() {
    this.messageObj.text = "Howdy!! There, how are you???";
  }

  updateMessageArray() {
     this.messageList.push("Howdy!!");      
  }

  updateMessageString() {
    this.parentMessage = "New message for you";
  }

We see below how each button click calls update function and which in turn updates value of our properties and our view updates accordingly. Let’s talk about how Angular updates the view of our components when the value of input properties passed to component change.

 

 

Angular uses Change Detection to update the view/DOM whenever data is changed.

Angular provide two strategies for Change Detection :

  • Default: In case of ChangeDetectionStrategy.Default strategy, whenever any data is mutated or changed anywhere in the application, Angular will run the change detector to update the view/DOM(Document Object Model).

Whenever any of the @Input property is changed or mutated, the Angular change detector will start form the root component and traverse all child components, evaluating and checking each bound expression of all input properties to update the view/DOM.

 Since single property change can cause change detector to traverse through the entire component tree, change detection may cause performance degradation of application. To avoid this, there is a way to instruct Angular that change detector should run for a component and its sub-tree only when new references are passed to them versus when data is simply mutated. This can be done by setting change detection strategy to OnPush.

  • OnPush:  Angular will only run the change detector when a new reference is passed to @Input() data.

By using ChangeDetectionStrategy.OnPush, Angular will only check the tree if the reference passed to the component is changed instead of some property change in the object as we have done at updateMessageObject() method above or adding a new value to array at updateMessageArray() method using push(), where reference to array stays same. String update at updateMessageString() will work same as in case of default change detection strategy. To implement OnPush let’s update our code.

To enable OnPush Change Detection on child component import ChangeDetectionStrategy from @angular/core  and set  “changeDetection” Metadata at component decorator to ChangeDetectionStrategy.OnPush.

my-child.component.ts

import { Component, OnInit, Input, ChangeDetectionStrategy } from '@angular/core';
import { Message } from '../my-child/message';

@Component({
  selector: 'app-my-child',
  templateUrl: './my-child.component.html',
  styleUrls: ['./my-child.component.css'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class MyChildComponent implements OnInit {
  @Input() message:string; 
  @Input() messageObj:Message;
  @Input() messageList:any;

Now if we click button to update values for our input properties, change detection will not trigger for the child component tree and values will not update for messageObject and messageList. Reason is that for object and array only value will change but reference is same as these properties are mutated. Since the change detection strategy is set to onPush, now the change detector will only run when the reference of the @Input property is changed.

 

 

Let us now change the object reference by assigning a new object to “messageObject”  at “updateMessageObject()” function as shown is the snippet below. Now if you click Update Object button on parent it will trigger change detection and message on child view will update to reflect change in input property.

my-parent.component.ts

updateMessageObject() {
  this.messageObj = {
      text: "Howdy!! There, how are you???",
      author: "Tony"
    }
  }

 

 

To update message list at “updateMessageArray()” we will use array prototype concat() method as it returns a new array instead of push() that will update the same array value.

my-parent.component.ts

updateMessageArray() {
  this.messageList = this.messageList.concat("How are you?");    
}

Array Method concat() will return a new array that will be assigned to “messageList”, there by changing the reference of the bound input property. Now if you click Update Array button on parent it will trigger change detection and message list on child view will update to reflect change in list of array items.

 

 

As seen in example above, we changed the bound input property of child component by changing the reference of the object/array instead of just mutating them. At this point, on click of button view/DOM is updated with the new value as shown in above videos for OnPush Change Detection Strategy. Using OnPush Change Detection will trigger change detection for the component tree only if reference to bound input properties change, thereby improving application performance.

We can further improve performance by using RxJS Observables because they emit new values without changing the reference of the object. We can subscribe to the observable for new value and then manually run ChangeDetector. We will cover an example for using Observables and manually running ChangeDetector in a separate post in near future.

Share this:

Leave a Reply

Your email address will not be published. Required fields are marked *