liveBook · Manning

ngDoCheck

As explained in Pro Angular 16

ngDoCheck is an Angular component lifecycle method that runs during change detection to support custom checking of state changes.

`ngDoCheck` is an Angular lifecycle method for custom change checks in a directive or component. It is useful when items are added to, removed from, or replaced within a collection: those changes do not invoke an iterating directive’s `ngOnChanges` method. `ngDoCheck` runs whenever Angular detects a change in the application, so the check must be inexpensive and updates should happen only when needed. section 15.3.4

An iterator that does too much work

A structural iterator can call `updateContent()` from both `ngOnInit()` and `ngDoCheck()`. If `updateContent()` clears its `ViewContainerRef` and recreates an embedded view for every item, even an unrelated change elsewhere in the application rebuilds the displayed collection. Restoring a product form alongside the table makes those repeated calls apparent while interacting with the form. section 15.3.4

Listing 15.15. Using ngDoCheck in the iterator.directive.ts File in the src/app folder
import { Directive, ViewContainerRef, TemplateRef, Input} 
    from "@angular/core";
import { interval } from "rxjs";
 
@Directive({
    selector: "[paForOf]"
})
export class PaIteratorDirective {
 
    constructor(private container: ViewContainerRef,
        private template: TemplateRef<Object>) { }
 
    @Input("paForOf")
    dataSource: any;
 
    ngOnInit() {
        this.updateContent();
    }

    ngDoCheck() {
        console.log("ngDoCheck Called");
        this.updateContent();
    }

    private updateContent() {
        this.container.clear();
        for (let i = 0; i < this.dataSource.length; i++) {
            this.container.createEmbeddedView(this.template,
                 new PaIteratorContext(this.dataSource[i],
                     i, this.dataSource.length));
        }
    }
}

class PaIteratorContext {
    odd: boolean; even: boolean;
    first: boolean; last: boolean;

    constructor(public $implicit: any,
            public index: number, total: number ) {

        this.odd = index % 2 == 1;
        this.even = !this.odd;
        this.first = index == 0;
        this.last = index == total - 1;

        // interval(2000).subscribe(() => {
        //     this.odd = !this.odd; this.even = !this.even;
        //     this.$implicit.price++;            
        // })
    }
}

The initial iterator logs each `ngDoCheck` call and rebuilds all its embedded views. Use it to identify the work that must not run unconditionally. section 15.3.4

A more selective iterator creates an `IterableDiffer` for its data source in `ngOnInit()`. During `ngDoCheck()`, it calls `diff(this.dataSource)` and processes records only when the result is not null. The addition example uses `forEachAddedItem()` and each record’s `item` and `currentIndex` to create views; handling deletion requires processing removals too. The differ separates frequent checking from changes that actually require a view update. section 15.3.4

Table 15.4. The IterableChanges methods and properties
Name
Description
forEachItem(func)
This method invokes the specified function for each object in the collection.
forEachPreviousItem(func)
This method invokes the specified function for each object in the previous version of the collection.
forEachAddedItem(func)
This method invokes the specified function for each new object in the collection.
forEachMovedItem(func)
This method invokes the specified function for each object whose position has changed.
forEachRemovedItem(func)
This method invokes the specified function for each object that was removed from the collection.
forEachIdentityChange(func)
This method invokes the specified function for each object whose identity has changed.

The `IterableChanges` method table distinguishes added, removed, moved, and identity-changed items when deciding which updates an iterator must handle. section 15.3.4

Refreshing a product table

A product-table component uses an `IterableDiffer` to check repository products inside `ngDoCheck()`. When the collection changes, it refreshes its `MatTableDataSource<Product>` so the Angular Material table reflects the repository. This applies the same hook to a component rather than a structural directive. section 7.5.3

Use `ngDoCheck` to decide whether work is necessary, not as a reason to repeat that work on every call. Conventional change detection still applies to directives whose data source is read from a signal in the described setup. section 15.3.4

How Pro Angular 16 covers it

How deep it goes
Works through unconditional rebuilding, iterable differ initialization, change records, and selective view updates; warns about repeated work.
Examples
Uses a product iterator beside a form and a repository-backed Angular Material product table.
What it assumes
Assumes familiarity with Angular components or directives, template bindings, and collections; demonstrates creating and using an iterable differ.
What you can do after
Apply it via Angular `IterableDiffers` to update collection-driven views or refresh a table only when collection changes are found.
How it connects
Connects `ngOnChanges`, `ngOnInit`, `IterableDiffers`, structural directives, `ViewContainerRef`, and `MatTableDataSource`.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Pro Angular 16. The section links point to the parts of the book it draws on.