Asool
Asool

Reputation: 14209

ngAfterViewInit vs ngAfterContentChecked

This question is related to a question that has been asked on StackOverflow quite a few times.

Basically, when you do something like

@Component({
  selector: 'my-app',
  template: `<div>I'm {{message}} </div>`,
})
export class App {
  message:string = 'loading :(';

  ngAfterViewInit() {
    this.updateMessage();
  }

  updateMessage(){
    this.message = 'all done loading :)'
  }
}

You get an error saying "Expression has changed after it was checked" . The reason you get this error has been explained in this post: Expression ___ has changed after it was checked

My question is, why does the following work?

@Component({
  selector: 'my-app',
  template: `<div>I'm {{message}} </div>`,
})
export class App {
  message:string = 'loading :(';

  ngAfterContentChecked() {
    this.updateMessage();
  }

  updateMessage(){
    this.message = 'all done loading :)'
  }
}

Why does this not return an error, and actually returns the proper result? Why does ngAfterViewChecked work, and ngAfterViewInit not work?

Explain like I'm 5.

Upvotes: 2

Views: 2081

Answers (1)

Darren Ruane
Darren Ruane

Reputation: 2505

The ngAfterViewInit and ngAfterContentChecked callbacks are Lifecycle Hooks in Angular. That is, they are called at certain, specific times during the lifecycle of your Angular Components and Directives.

The behaviour you are experiencing stems from the point in time in which Angular's Change Detection runs in relation to the point in time that each of these Lifecycle Hooks are invoked.

More specifically, the first iteration of change detection runs right after the first ngAfterContentChecked hook but right before the call to ngAfterViewInit.

I've created a Stackblitz Demo here (look in the console) to illustrate the flow of calls to these Lifecycle Hooks and the Change Detection runs, but I will also summarise my findings here:

By changing the value of message inside of the ngAfterContentChecked hook, its value will be set before the first iteration of change detection runs and spots it. This means that on subsequent change detection runs, it will have the same value as it had during the first. This is good and exactly what the Change Detector wants.

In contrast, by setting the value of message inside of the ngAfterViewInit hook, its value will be set after the first iteration of change detection runs. In other words, when the first iteration runs and evaluates message, it will see that it has a value of loading :(, directly afterwards ngAfterViewInit is called which changes its value. Subsequent change detection runs will evaluate message as all done loading :) which is of course different from the previous iterations loading :( value and thus, an ExpressionChangedAfterItHasBeenCheckedError is thrown.

Upvotes: 5

Related Questions