Angular change detection -OnPush

nikhildhawan

Nikhil Dhawan

Posted on July 6, 2021

Angular change detection -OnPush

In my last post about change detection, we touched on the basics of it. In this article, we will try to understand more about ChangeDetectionStrategy.OnPush method. So let us get started.

About OnPush

So first thing we will discuss is how to convert the component to the Onpush strategy. Let say we have a Sample component and we are having a default change detection strategy(by default all newly created components are having default change detection), it will be like:

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

@Component({
  selector: 'app-sample',
  templateUrl: './sample.component.html',
  styleUrls: ['./sample.component.scss']
})
export class SampleComponent implements OnInit {

  constructor() { }

  ngOnInit(): void {
  }

}
Enter fullscreen mode Exit fullscreen mode

Now let's add the change detection as OnPush to this component, we can do this by adding property name changeDetection to the component metadata with value as ChangeDetectionStrategy.OnPush.
If you see available values with the help of IDE support for this strategy you will be able to see that another value is Default( you don't need to set this explicitly if you want the default way of change detection)

image

and our component code will be as below now

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

@Component({
  selector: 'app-sample',
  templateUrl: './sample.component.html',
  styleUrls: ['./sample.component.scss'],
  changeDetection:ChangeDetectionStrategy.OnPush
})
export class SampleComponent implements OnInit {

  constructor() { }

  ngOnInit(): void {
  }

}
Enter fullscreen mode Exit fullscreen mode

Now we have successfully made the change we want, now in the next section let's see which conditions will trigger the component to rerender.

When will change detection happen for the component

So after we have done as above, the component will not be rerendered on every change detection but only when the input is changed from the parent component or parameters are changed inside the component itself that will rerender it and its child components.
Let's see it with an example, Full code can be seen on Stackblitz. I will use snippets from that here.
Let's say I have a component name Sample Component and it has a child component as SampleChild which has OnPush implemented.
Firstly let's make the change in a mutable way, we are using the value in the object here.
sample.component

<button (click)="valueChange()" >
  Change input to 5
</button>
<app-samplechild [data]="data" ></app-samplechild>
Enter fullscreen mode Exit fullscreen mode
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-sample',
  templateUrl: './sample.component.html',
  styleUrls: ['./sample.component.scss']
})
export class SampleComponent implements OnInit {
  data={value:1};
  constructor() { }

  ngOnInit(): void {
  }
  valueChange(){
    this.data.value=5;
  }
}
Enter fullscreen mode Exit fullscreen mode

samplechild.component

<p>
  The value from parent is
  {{data.value}}
</p>
<p>

  {{whenComponentRerendered()}}
</p>
Enter fullscreen mode Exit fullscreen mode
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';

@Component({
  selector: 'app-samplechild',
  templateUrl: './samplechild.component.html',
  styleUrls: ['./samplechild.component.scss'],
  changeDetection:ChangeDetectionStrategy.OnPush
})
export class SamplechildComponent implements OnInit {

  @Input('data') data!:any;
  constructor() { }

  ngOnInit(): void {
  }
  whenComponentRerendered(){
    console.log('component rerendered');
  }
}
Enter fullscreen mode Exit fullscreen mode

Now you will notice that even on clicking the button to change the input the value in the child component will not change that is it is not rerendered due to the use of the OnPush strategy. You can experiment here by changing OnPush to Default, you will notice the value gets updated in the child component i.e. it is rerendered.

So now how to update the value in the child component without changing the onPush strategy, the one rule here is to always use an immutable way of passing input objects like instead of modifying object directly pass the new reference of the object. Let's modify our code accordingly in the parent component.

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

@Component({
  selector: 'app-sample',
  templateUrl: './sample.component.html',
  styleUrls: ['./sample.component.scss']
})
export class SampleComponent implements OnInit {
  data={value:1};
  constructor() { }

  ngOnInit(): void {
  }
  valueChange(){
    this.data={
      ...this.data,
      value:5
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Now with the usage of the immutable way of the passing object, the view will be updated with the value 5.
By this, the change detection will not happen in the child component when change detection happens in the parent, for demonstrating it I have added a simple button which just consoles the log and other function which will be consoling the log when the component is rerendered for both child and parent components So when we click on that newly added button, the parent component is rerendered but not the child, see the screenshot below.
image

The child component can also have change detection executed when the event or change is from child component itself like we change the value from child component , lets add the below code change to the child component.

<p>
  The value from parent is
  {{ data.value }}
</p>
<p>
  {{ whenComponentRerendered() }}
</p>
<button (click)="changeValue()">Change button from child component</button>
Enter fullscreen mode Exit fullscreen mode
import {
  ChangeDetectionStrategy,
  Component,
  Input,
  OnInit,
} from '@angular/core';

@Component({
  selector: 'app-samplechild',
  templateUrl: './samplechild.component.html',
  styleUrls: ['./samplechild.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SamplechildComponent implements OnInit {
  @Input('data') data!: any;
  constructor() {}

  ngOnInit(): void {}
  whenComponentRerendered() {
    console.log('child component rerendered');
  }
  changeValue() {
    this.data.value = 5;
  }
}
Enter fullscreen mode Exit fullscreen mode

So now when we click on the button in the child component that component will be rerendered and that we can validate y seeing the console outputs
image

So in this article, we discussed OnPush change detection strategy implementation for our component and what conditions trigger the change detection. In the upcoming article in this series, I will discuss what are the other ways we can take control of change detection. Thanks for reading.

If you liked it please share it with your friends or if any suggestions reach me out on Twitter or comment below.
Till next time Happy Learning!

💖 💪 🙅 🚩
nikhildhawan
Nikhil Dhawan

Posted on July 6, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Angular Form Array
angular Angular Form Array

November 29, 2024

Can a Solo Developer Build a SaaS App?
undefined Can a Solo Developer Build a SaaS App?

November 29, 2024

Angular's New Feature: Signals
javascript Angular's New Feature: Signals

November 29, 2024