In this tutorial, we are going to learn how to use the ngDoCheck lifecycle hook in angular.
We will also see how to keep track of the changes through the component Inputs using the ngDoCheck
callback.
This will lead us to learn the difference between the ngDoCheck lifecycle hook and the ngOnChanges that we have seen in the previous tutorial.
Table of Contents
Angular Lifecycle Hooks
Angular has 8 lifecycle hooks, but in this tutorial, we will cover only the ngDoCheck lifecycle hook
What is ngDoCheck lifecycle hook ?
ngDoCheck
is a callback that is used to implement our custom change detection.
If we check the documentation ,we will see that many directives are implementing the ngDoCheck lifecycle hook to perform custom change detection.
We can mention : NgClass
, NgForOf
, NgStyle
, NgSwitchCase
To correctly use the ngDoCheck lifecycle hook , we need to use IterableDiffers
and KeyValueDiffers
.
KeyValueDiffers : is used to detect changes in the object properties and the changes in the array at the same time.
IterableDiffers : is used only to check the changes in iterable objects ( Array ..)
ngDocheck
is a simple method that exists in the doCheck
Interface , that’s why we need to implement the doCheck interface in our component to correctly use the ngDoCheck lifecycle hook.
The ngDoCheck lifecycle hook callback is triggered before the component is being checked. that allow us to perform custom logic and then mark component and it’s children for check in the next change detection cycle.
The ngDoCheck lifecycle hook callback is immediately fired when angular detects any changes of the data-bound input property.
The ngDoCheck callback runs just after the ngOnChanges callback using a different change detection system.
According to the doc ,it is recommended to use only one of them in your component because they are both working with the component input properties.
Let’s see in the next step , the difference between the ngDoCheck
and ngOnChanges.
The difference between ngDoCheck and ngOnChanges
In Angular , we can handle the change detection using two different lifecycle hooks : the ngOnchanges and the ngDoCheck lifecycle hook.
So why does angular provide two lifecycle hooks if both of them are doing the same job?
when we use ngOnChanges lifecycle hook , angular will perform the default change detection
That’s why , In some cases, the ngOnChanges
is not enough to detect all changes because it uses the property reference to check the difference, so it can’t detect the changes if you update the object property.
Meanwhile, the ngDoCheck lifecycle hook allows us to perform our custom change detection so we can bypass the ngOnChange lifecycle hook limitations.
So , if angular fails to detect changes using the ngOnChanges you can use the ngDoCheck lifecycle hook to detect them.
Check this article speaking with more details about the difference between ngOnChanges and ngDoCheck lifecycle hooks.
How to use the ngDoCheck lifecycle hook ?
Now we are going to make an example to learn how to use the ngDoCheck lifecycle hook step by step.
As we said in the previous sections, ngDoCheck and ngOnChanges callbacks are fired when angular detects any changes of the data-bound input property.
So that we need to create a parent component and pass values to the child component using the input data-bound.
Parent component :
import {Component, OnInit} from '@angular/core';
import {FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms";
export class User {
pseudo: string | undefined ;
firstName: string| undefined ;
lastName: string| undefined ;
email: string | undefined ;
}
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent implements OnInit {
userForm: FormGroup = new FormGroup({});
userData: User = new User();
constructor(private formBuilder: FormBuilder) {
}
ngOnInit() {
this.initializeForm();
}
initializeForm(): void {
this.userForm = this.formBuilder.group({
pseudo: new FormControl('', Validators.required),
firstName: new FormControl('', Validators.required),
lastName: new FormControl('', Validators.required),
email: new FormControl('', Validators.required),
});
}
saveUser(): void {
this.userData = this.userForm.getRawValue();
}
}
Now we will create the child component and we pass the user object from the parent.
child component :
import {Component, DoCheck, Input, OnInit} from "@angular/core";
@Component({
selector: 'app-child',
templateUrl: './child.component.html',
})
export class ChildComponent implements OnInit ,DoCheck{
@Input() user: User | undefined;
changelogList : Array = [];
oldUser: User | undefined;
constructor() {
}
ngOnInit() {
this.oldUser = Object.assign({}, this.user);
}
ngDoCheck() {
if (this.oldUser?.pseudo !== this.user?.pseudo) {
const previous = JSON.stringify(this.oldUser);
const current = JSON.stringify(this.user);
this.changelogList.push(`changes detected from ${previous} to ${current}`);
this.oldUser = Object.assign({}, this.user);
}
}
}
User Info :
pseudo : {{ user?.pseudo }}
firstName : {{ user?.firstName }}
lastName : {{ user?.lastName }}
email : {{ user?.email }}
Change Log :
- {{ logMessage }}
Now, if we run the example , we will get something like this :
In the previous example , everything is working well but unfortunately we didn’t use the ngDoCheck callback in the best way.
Since we know that the ngDoCheck callback is called very frequently by angular, we just checked and compared the current value and the last value of the Input using local objects.
So , to correctly use the ngDoCheck , we can use key-value differs and iterable differs services to perform custom change detections.
ngDoCheck using Key-value differs
In this section we are going to use the key-value differs service to handle the change detection.
So we will just update the child component :
1- Inject the service into the constructor
constructor(private differs: KeyValueDiffers) {
}
2- Create the differ and init the property inside the ngOnInit lifecycle hook.
differ: any;
ngOnInit() {
this.differ = this.differs.find(this.user).create();
}
3- Use the diff method inside ngDoCheck callback to check if the user object is changed.
the diff method returns the changes object or null ( if there are no changes )
the changes object provides multiples methods such as
forEachItem, forEachPreviousItem, forEachChangedItem, forEachAddedItem, forEachRemovedItem.
//child-component.ts
import {Component, DoCheck, Input, KeyValueDiffers, OnInit} from "@angular/core";
@Component({
selector: 'app-child',
templateUrl: './child.component.html',
})
export class ChildComponent implements OnInit, DoCheck {
@Input() user: any;
changelogList: Array = [];
differ: any;
constructor(private differs: KeyValueDiffers) {
}
ngOnInit() {
this.differ = this.differs.find(this.user).create();
}
ngDoCheck() {
const userChanges = this.differ.diff(this.user);
if (userChanges) {
userChanges.forEachChangedItem((changeRecord: any) => {
this.changelogList.push('item changed : ' + changeRecord.key + ' ' + JSON.stringify(changeRecord.currentValue))
});
userChanges.forEachAddedItem((changeRecord: any) => {
this.changelogList.push('item added : ' + changeRecord.key + ' ' + JSON.stringify(changeRecord.currentValue))
});
}
}
}
References
Recapitulation
In this tutorial , we have seen how to use the ngDoCheck lifecycle hook to perform change detection using two different ways.
The first way is using a manual check of the changes of the data-bound input property using the local variables and the second way that i recommend is using the key-value differs and iterable differs services that provides multiple advanced methods to check object changes.
Brief :
- ngDoCheck is a callback that is used to implement our custom change detection
- ngDoCheck callback is triggered before the component is being checked
- ngDoCheck callback is immediately fired when angular detects any changes of the data-bound input property
- ngDoCheck callback runs just after the ngOnChanges callback using a different change detection system.
- we need to use key-value differs and iterable differs services to perform custom change detections
- KeyValueDiffers : is used to detect changes in the object properties and the changes in the array at the same time
- IterableDiffers : is used only to check the changes in iterable objects ( Array ..)
Clone the project
To download and run the example, please follow the steps :
1- Clone the source code from XperTuto git repository using the following command :
git clone https://github.com/www-xperTuto-com/ngDoCheck_lifecycle_hook.git
2- Install the node dependencies using NPM command line : npm install
3- Run the application using the following command : ng serve
4- Access to the project through the URL : http://localhost:4200
Read More About Angular Lifecycle hooks:
- How to use ngAfterViewInit lifecycle hook ?
- How to use ngOnChanges lifecycle hook ?
- How to use ngOnInit lifecycle hook ?
- How to use ngDoCheck lifecycle hook in angular ?
- How to use ngAfterContentInit lifecycle hook ?
- How to use ngAfterContentChecked lifecycle hook ?
- How to use ngAfterViewChecked lifecycle hook ?
- How to use ngOnDestroy lifecycle hook ?