Expression ___ has changed after it was checked

2018-12-31 16:40发布

Why is the component in this simple plunk

@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 :)'
  }
}

throwing:

EXCEPTION: Expression 'I'm {{message}} in App@0:5' has changed after it was checked. Previous value: 'I'm loading :( '. Current value: 'I'm all done loading :) ' in [I'm {{message}} in App@0:5]

when all I'm doing is updating a simple binding when my view is initiated?

13条回答
流年柔荑漫光年
2楼-- · 2018-12-31 16:51

For this I have tried above answers many does not work in latest version of Angular (6 or later)

I am using Material control that required changes after first binding done.

    export class AbcClass implements OnInit, AfterContentChecked{
        constructor(private ref: ChangeDetectorRef) {}
        ngOnInit(){
            // your tasks
        }
        ngAfterViewInit() {
            this.ref.detectChanges();
        }
    }

Adding my answer so, this helps some solve specific issue.

查看更多
唯独是你
3楼-- · 2018-12-31 16:52

It throw an error because your code get updated when ngAfterViewInit() is called. Mean your initial value got changed when ngAfterViewInit take place, If you call that in ngAfterContentInit() then it will not throw an error.

ngAfterContentInit() {
    this.updateMessage();
}
查看更多
只靠听说
4楼-- · 2018-12-31 16:55

I couldnt comment on @Biranchi s post since I dont have enough reputation, but it fixed the problem for me.

One thing to note! If adding changeDetection: ChangeDetectionStrategy.OnPush on the component didn't work, and its a child component (dumb component) try adding it to the parent also.

This fixed the bug, but I wonder what are the side effects of this.

查看更多
不再属于我。
5楼-- · 2018-12-31 17:00

As stated by drewmoore, the proper solution in this case is to manually trigger change detection for the current component. This is done using the detectChanges() method of the ChangeDetectorRef object (imported from angular2/core), or its markForCheck() method, which also makes any parent components update. Relevant example:

import { Component, ChangeDetectorRef } from 'angular2/core'

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

  constructor(private cdr: ChangeDetectorRef) {}

  ngAfterViewInit() {
    this.message = 'all done loading :)'
    this.cdr.detectChanges();
  }

}

Here are also Plunkers demonstrating the ngOnInit, setTimeout, and enableProdMode approaches just in case.

查看更多
路过你的时光
6楼-- · 2018-12-31 17:05

You can also put your call to updateMessage() in the ngOnInt()-Method, at least it works for me

ngOnInit() {
    this.updateMessage();
}

In RC1 this does not trigger the exception

查看更多
还给你的自由
7楼-- · 2018-12-31 17:06

You just have to update your message in the right lifecycle hook, in this case is ngAfterContentChecked instead of ngAfterViewInit, because in ngAfterViewInit a check for the variable message has been started but is not yet ended.

see: https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html#!#afterview

so the code will be just:

import { Component } from 'angular2/core'

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

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

see the working demo on Plunker.

查看更多
登录 后发表回答