Angular ngDoCheck()
September 28, 2019
Angular ngDoCheck()
is a callback method that performs custom change-detection. The ngDoCheck()
is the method of DoCheck
interface. The DoCheck
is a lifecycle hook that invokes a custom change-detection function for a directive. The Angular classes NgClass
, NgForOf
, NgStyle
, NgSwitchCase
and UpgradeComponent
implement DoCheck
interface to perform change-detection. Angular also performs default change-detection an invokes ngOnChanges()
callback which is the method of OnChanges
lifecycle hook. The ngDoCheck()
callback method is run after ngOnChanges()
callback method. According to Angular doc, we should not use both ngDoCheck()
and ngOnChanges()
callback methods to respond to changes on the same input. To perform change-detection with ngDoCheck()
, we need to use KeyValueDiffers
and IterableDiffers
for implementing custom change checking for collections. The KeyValueDiffers
can detect the changes in Array as well as changes in properties of objects whereas IterableDiffers
can only detect changes in iterable objects such as Array.
To use
ngDoCheck()
, our components need to implement DoCheck
. Find the code snippet.
import { Component, DoCheck } from '@angular/core'; @Component({ ------ }) export class EmployeeComponent implements DoCheck { ngDoCheck() { } }
ngDoCheck()
with KeyValueDiffers
class to detect changes in Array while adding and removing elements and to detect changes in property value of objects of Array. We will also create IterableDiffers
class example to detect changes in Array.
Contents
1. Technologies Used
Find the technologies being used in our example.1. Angular 8.0.3
2. TypeScript 3.4.3
3. Node.js 12.5.0
4. Angular CLI 8.0.6
2. ngDoCheck() with KeyValueDiffers
KeyValueDiffers
is the repository of different map diffing strategies. Angular uses it internally for directives NgClass
, NgStyle
etc. The KeyValueDiffers
can detect the changes in Array as well as changes in properties of objects. Find the steps to detect changes using KeyValueDiffers
.
1.
KeyValueDiffers
can be injected into component using constructor.
constructor(private kvDiffers: KeyValueDiffers) {}
KeyValueDiffers
provides find
method.
find(kv: any): KeyValueDifferFactory
find
method returns KeyValueDifferFactory
.
3.
KeyValueDifferFactory
provides factory for KeyValueDiffer
. To get KeyValueDiffer
instance, KeyValueDifferFactory
provides create
method.
create<K, V>(): KeyValueDiffer<K, V>
KeyValueDiffer
.
this.arrayDiffer = this.kvDiffers.find(this.empArray).create();
arrayDiffer
is the instance of KeyValueDiffer
.
4.
KeyValueDiffer
is a differ that tracks changes made to an object over time. It has a diff
method to compute a difference between the previous state and the new object state.
diff(object: { [key: string]: V; }): KeyValueChanges<string, V> | null
KeyValueChanges
.
let empArrayChanges = this.arrayDiffer.diff(this.empArray);
empArrayChanges
is the instance of KeyValueChanges
.
5.
KeyValueChanges
keeps the changes in the map since the last time the method diff
was invoked. KeyValueChanges
has following methods.
forEachItem: Iterates over all changes.
forEachPreviousItem: Iterates over previous items that has changed.
forEachChangedItem: Iterates those items whose value is changed.
forEachAddedItem: Iterates over all added items.
forEachRemovedItem: Iterates over all removed items.
All the above methods of
KeyValueChanges
provides KeyValueChangeRecord
item in iteration.
6.
KeyValueChangeRecord
is the record representing the item change information. KeyValueChangeRecord
has properties such as currentValue
and previousValue
. To get current value, we call its currentValue
property and to get previous value, we call its previousValue
property. Find the code snippet.
if (empArrayChanges) { empArrayChanges.forEachAddedItem((record) => { let emp = record.currentValue; console.log('Added ' + emp.name); }); }
2.1 Detect Changes in Array with KeyValueChanges
Suppose we have an array ofEmployee
class and we want to detect changes in the array whenever an item is added or removed. We can detect changes using KeyValueDiffers
as following.
@Input() empArray: Employee[]; constructor(private kvDiffers: KeyValueDiffers) {} ngOnInit() { this.arrayDiffer = this.kvDiffers.find(this.empArray).create(); } ngDoCheck() { const empArrayChanges = this.arrayDiffer.diff(this.empArray); if (empArrayChanges) { empArrayChanges.forEachAddedItem((record) => { let emp = record.currentValue; console.log('Added ' + emp.name); }); empArrayChanges.forEachRemovedItem((record) => { let emp = record.previousValue; console.log('Removed ' + emp.name); }); } }
Employee
object of the array changes value of its any property.
2.2 Detect Changes in Objects inside Array with KeyValueChanges
Suppose we have an array ofEmployee
class and we want to detect changes when any Employee
object of the array changes value of its any property. We can detect changes using KeyValueDiffers
as following.
@Input() empArray: Employee[]; empDifferMap = new Map<number, any>(); empMap = new Map<number, Employee>(); arrayDiffer: any; constructor(private kvDiffers: KeyValueDiffers) {} ngOnInit() { this.empArray.forEach(emp => { this.empDifferMap[emp.id] = this.kvDiffers.find(emp).create(); this.empMap[emp.id] = emp; }) } ngDoCheck() { for (let [key, empDiffer] of this.empDifferMap) { let empChanges = empDiffer.diff(this.empMap.get(key)); if (empChanges) { empChanges.forEachChangedItem(record => { console.log('Previous value: ' + record.previousValue); console.log('Current value: ' + record.currentValue); }); } } }
3. ngDoCheck() with IterableDiffers: Detect Changes in Array
TheIterableDiffers
is the repository of different iterable diffing strategies used by directives NgFor
, NgClass
etc. The IterableDiffers
can only detect changes in iterable objects such as Array. Find the steps to detect changes using IterableDiffers
.
1.
IterableDiffers
can be injected into component using constructor.
constructor(private itrDiffers:IterableDiffers) {}
IterableDiffers
provides find
method.
find(iterable: any): IterableDifferFactory
find
method returns IterableDifferFactory
.
3.
IterableDifferFactory
provides factory for IterableDiffers
. To get IterableDiffer
instance, IterableDifferFactory
provides create
method.
create<V>(trackByFn?: TrackByFunction<V>): IterableDiffer<V>
IterableDiffer
.
this.empDiffer = this.itrDiffers.find([]).create(null);
empDiffer
is the instance of IterableDiffer
.
4.
IterableDiffer
is a differ that tracks changes made to an iterable object over time. It has a diff
method to compute a difference between the previous state and the new object state.
diff(object: NgIterable<V>): IterableChanges<V> | null
IterableChanges
.
const empArrayChanges = this.empDiffer.diff(this.empArray);
empArrayChanges
is the instance of IterableChanges
.
5.
IterableChanges
is an object describing the changes in the iterable collection since last time diff()
was invoked. IterableChanges
has following methods.
forEachItem: Iterates over all changes.
forEachOperation: Iterates over a set of operations.
forEachPreviousItem: Iterates over previous items that has changed.
forEachAddedItem: Iterates over all added items.
forEachMovedItem: Iterates over all moved items.
forEachRemovedItem: Iterates over all removed items.
forEachIdentityChange: Iterate over all items which had their identity changed.
All the above methods of
IterableChanges
provides IterableChangeRecord
item in iteration.
6.
IterableChangeRecord
is the record representing the item change information. IterableChangeRecord
has properties such as currentIndex
, previousIndex
, item
and trackById
. To get value, we call its item
property.
7. Find the code snippet to detect changes in Array. Suppose we have an array of
Employee
class and we want to detect changes in the Array whenever an item is added or removed.
@Input() empArray: Employee[]; empDiffer: any; constructor(private itrDiffers:IterableDiffers) { } ngOnInit() { this.empDiffer = this.itrDiffers.find([]).create(null); } ngDoCheck() { const empArrayChanges = this.empDiffer.diff(this.empArray); if (empArrayChanges) { empArrayChanges.forEachAddedItem(record => { let emp = record.item; console.log('Added ' + emp.name); }); empArrayChanges.forEachRemovedItem(record => { let emp = record.item; console.log('Removed ' + emp.name); }); } }
4. Complete Example
employee.tsexport class Employee { constructor(public id: number, public name: string){} }
import { Component, OnInit } from '@angular/core'; import { Employee } from './employee'; @Component({ selector: 'app-root', template: ` <h2>ngDoCheck() Demo</h2> <table border="1" cellpadding="8" cellspacing="0"> <tr *ngFor="let emp of empArray; let i = index"> <td>{{emp.id}}</td><td>{{emp.name}}</td><td><button (click)="remove(i)">Remove</button></td> <td><button (click)="update(i)">Update</button></td> </tr> </table> <button (click)="add()">Add</button> <h3>Change Logs</h3> <table border="1" cellpadding="8" cellspacing="0"> <tr> <td><b>Using KeyValueDiffers</b></td> <td><b>Using IterableDiffers</b></td> </tr> <tr> <td><app-emp-kv [empArray]="empArray"></app-emp-kv></td> <td><app-emp-itr [empArray]="empArray"></app-emp-itr></td> </tr> </table> ` }) export class AppComponent implements OnInit { empArray = []; index = 103; ngOnInit() { this.empArray.push(new Employee(100, "Mahesh")); this.empArray.push(new Employee(101, "Krishna")); this.empArray.push(new Employee(102, "Shiva")); } add() { this.empArray.push(new Employee(this.index, "Name"+ this.index++)); console.log('Employee added: ' + JSON.stringify(this.empArray)); } remove(index) { console.log(index); this.empArray.splice(index, 1); } update(index) { this.empArray[index].name += "-U"; } }
import { Component, DoCheck, KeyValueDiffers, OnInit, Input } from '@angular/core'; import { Employee } from './employee'; @Component({ selector: 'app-emp-kv', template: ` <div *ngFor="let log of kvChangeLogs"> {{log}} </div> ` }) export class EmployeeKVDiffComponent implements DoCheck, OnInit { @Input() empArray: Employee[]; empDifferMap = new Map<number, any>(); empMap = new Map<number, Employee>(); arrayDiffer: any; kvChangeLogs: string[] = []; constructor(private kvDiffers: KeyValueDiffers) { } ngOnInit() { this.arrayDiffer = this.kvDiffers.find([]).create(); this.empArray.forEach(emp => { this.empDifferMap[emp.id] = this.kvDiffers.find(emp).create(); this.empMap[emp.id] = emp; }) } ngDoCheck() { //Detect changes in array when item added or removed const empArrayChanges = this.arrayDiffer.diff(this.empArray); if (empArrayChanges) { empArrayChanges.forEachAddedItem((record) => { let emp = record.currentValue; this.empDifferMap.set(emp.id, this.kvDiffers.find(emp).create()); this.empMap.set(emp.id, emp); this.kvChangeLogs.push('Added ' + emp.name); }); empArrayChanges.forEachRemovedItem((record) => { let emp = record.previousValue; this.empDifferMap.delete(emp.id); this.empMap.delete(emp.id); this.kvChangeLogs.push('Removed ' + emp.name); }); } //Detect changes in object inside array for (let [key, empDiffer] of this.empDifferMap) { const empChanges = empDiffer.diff(this.empMap.get(key)); if (empChanges) { empChanges.forEachChangedItem(record => { this.kvChangeLogs.push('---Update (id=' + key + ')---'); this.kvChangeLogs.push('Previous value: ' + record.previousValue); this.kvChangeLogs.push('Current value: ' + record.currentValue); this.kvChangeLogs.push('-------------------------'); }); } } } }
import { Component, DoCheck, IterableDiffers, OnInit, Input } from '@angular/core'; import { Employee } from './employee'; @Component({ selector: 'app-emp-itr', template: ` <div *ngFor="let log of itrChangeLogs"> {{log}} </div> ` }) export class EmployeeITRDiffComponent implements DoCheck, OnInit { @Input() empArray: Employee[]; itrChangeLogs: string[] = []; empDiffer: any; constructor(private itrDiffers: IterableDiffers) { } ngOnInit() { this.empDiffer = this.itrDiffers.find([]).create(null); } ngDoCheck() { const empArrayChanges = this.empDiffer.diff(this.empArray); if (empArrayChanges) { empArrayChanges.forEachAddedItem(record => { let emp = record.item; console.log('Added ' + emp.name); this.itrChangeLogs.push('Added ' + emp.name); }); empArrayChanges.forEachRemovedItem(record => { let emp = record.item; console.log('Removed ' + emp.name); this.itrChangeLogs.push('Removed ' + emp.name); }); } } }
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; import { AppComponent } from './app.component'; import { EmployeeITRDiffComponent } from './employee-itrdiff.component'; import { EmployeeKVDiffComponent } from './employee-kvdiff.component'; @NgModule({ imports: [ BrowserModule, FormsModule ], declarations: [ AppComponent, EmployeeITRDiffComponent, EmployeeKVDiffComponent ], providers: [ ], bootstrap: [ AppComponent ] }) export class AppModule { }
5. Run Application
To run the application, find the steps.1. Download source code using download link given below on this page.
2. Use downloaded src in your Angular CLI application. To install Angular CLI, find the link.
3. Run ng serve using command prompt.
4. Access the URL http://localhost:4200
Click on the add, remove and update button and we can see the logs of change-detection.

6. References
Angular doc: DoCheckAngular doc: KeyValueDiffers
Angular doc: IterableDiffers
Angular Lifecycle Hooks