Calling detectChanges() inside Angular2 lifecycle hooks?

1.2k Views Asked by At

What I know, is that we call detectChanges() to trigger local change detection checks. I have seen some code that detectChanges() is called inside ngOnChanges(change) hook and I was wondering why would someone want to do something like this? I mean, if the change detection was not detected, the ngOnChanges wouldn't be called right? So why to call detectChanges inside the hook's body?

@Directive({
    selector: "[dateDisplay]"
})
export class DateDisplayDirective {
    @Input() ngModel: Date;
    @Input() format: string;

    constructor(private _elem: ElementRef, private _change: ChangeDetectorRef) { }

    ngOnChanges(change) {
        if (change.ngModel && change.ngModel.currentValue && change.ngModel.currentValue != change.ngModel.previousValue) {
            let date = this.ngModel ? moment.utc(this.ngModel) : null;
            if (this._elem.nativeElement.value != undefined) {
                this._elem.nativeElement.value = date ? date.format(this.format) : "";
                this._change.detectChanges();
            }
        }
    }
}
1

There are 1 best solutions below

0
On

I think your intuition is right and this example seems not logical. Any change you'd make to properties relevant for your component's view will be still reflected, there's no need to retrigger change detection.

ngOnChanges() does not seem to be a good place to trigger change detection manually

Also, formatting a value for display should be done in pipe, not directive, so any problems that author of this code had with change detection might be solved if one would use a pipe!

So if one tries to do that in their code, one probably doesn't understand lifecycle hooks and change detection well enough and there is some other problem that needs to get addressed.

On the other hand, there are some other lifecycle hooks, for which there are valid cases to trigger CD manually.

From Angular documentation:

ngOnChanges()

Respond when Angular sets or resets data-bound input properties.

ngDoCheck()

Detect and act upon changes that Angular can't or won't detect on its own.

Let's assume you have @Inputs in your component and one of them is an object.

export class Hero  
{
    id:string;
    name:string; 
}

@Input() hero!: Hero;
@Input() power = '';

When you change hero's name (eg. typing it to some input on your page) change detection won't be triggered because from Angular point of view reference to hero object didn't change, so there's no need to run change detection.

But we know that the name changed and we wanna force angular to reflect that change. That's where ngDoCheck might come in handy.

  ngDoCheck() {
    this._change.detectChanges()
  }

Although this is very expensive. ngDoCheck() is called with enormous frequency — after every change detection cycle no matter where the change occurred. Just moving the mouse cursor into another input triggers a call.