expression式___在检查后发生了变化

为什么在这个简单的组成部分

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

投掷:

EXCEPTION:检查后,expression式“我在App @ 0:5中{{message}}”已被更改。 上一个值:'我正在加载:('。当前值:'我都加载完成了:)'[我是{{message}}在App @ 0:5]

当我所做的是更新一个简单的绑定,当我的观点开始?

如drewmoore所述,在这种情况下,正确的解决scheme是手动触发当前组件的变化检测。 这是通过使用detectChanges()对象的detectChanges()方法(从angular2/core导入的)或其markForCheck()方法完成的,该方法也会更新所有父组件。 相关示例 :

 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(); } } 

这里还有一些显示ngOnInit , setTimeout和enableProdMode方法的Plunkers以防万一。

首先,请注意,只有在开发模式下运行应用程序时,才会抛出此exception(默认情况下为beta-0):如果在引导应用程序时调用enableProdMode() ,将无法获取抛出( 见更新plunk )。

其次, 不要这样做,因为这个exception正在被抛出,原因很简单:总之,当处于开发模式时,每一轮更改检测都会立即进行第二轮检查,从第一轮检查结束后,因为这会表明变化是由变化检测本身引起的。

在你的plunk中,绑定{{message}}是通过调用setMessage()来改变的,这发生在ngAfterViewInit挂钩中,该挂钩是初始更改检测转向的一部分。 这本身并没有问题 – 问题是setMessage()改变了绑定,但不会触发新一轮的变化检测,这意味着这个变化将不会被检测到,直到未来一轮的变化检测触发到其他地方。

外卖: 任何改变绑定的东西都需要触发一轮更改检测

更新回应所有请求的例子如何做到这一点 :@ Tycho的解决scheme的作品,在答案的三种方法@MarkRajcok指出。 但坦率地说,他们都觉得我是丑陋的,不对的,就像我们习以为常的那种黑客。

可以肯定的是,在偶尔的情况下,这些黑客行为是恰当的,但是如果你在非常偶然的基础上使用它们,这表明你正在与框架作斗争,而不是完全接受它的react native本质。

恕我直言,更接近这种惯用的“Angular2方式”是沿着这样的方向:( plunk )

 @Component({ selector: 'my-app', template: `<div>I'm {{message | async}} </div>` }) export class App { message:Subject<string> = new BehaviorSubject('loading :('); ngAfterViewInit() { this.message.next('all done loading :)') } } 

你不能使用ngOnInit因为你只是改变成员variablesmessage

如果你想访问一个对子组件@ViewChild(ChildComponent)的引用,你确实需要用ngAfterViewInit来等待它。

一个肮脏的解决方法是在例如setTimeout的下一个事件循环中调用updateMessage()

 ngAfterViewInit() { setTimeout(() => { this.updateMessage(); }, 1); } 

我通过从angular core中添加ChangeDetectionStrategy来解决这个问题。

 import { Component, ChangeDetectionStrategy } from '@angular/core'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, selector: 'page1', templateUrl: 'page1.html', }) 

文章您需要了解的有关ExpressionChangedAfterItHasBeenCheckedError错误的所有信息都可以很好地解释这种行为。

您设置的问题是ngAfterViewInit生命周期挂钩在更改检测处理DOM更新后执行。 而且你正在有效地改变这个钩子模板中使用的属性,这意味着DOM需要被重新渲染:

  ngAfterViewInit() { this.message = 'all done loading :)'; // needs to be rendered the DOM } 

这将需要另一个更改检测周期,Angulardevise只运行一个摘要周期。

你基本上有两个select如何解决它:

  • 使用setTimeoutPromise.then或模板中引用的asynchronousobservable来asynchronous更新属性

  • 在DOM更新之前在钩子中执行属性更新 – ngOnInit,ngDoCheck,ngAfterContentInit,ngAfterContentChecked。

你也可以把你的调用方法中的updateMessage()放在ngOnInt()方法中,至less它适用于我

 ngOnInit() { this.updateMessage(); } 

在RC1中,这不会触发exception

您只需在正确的生命周期钩子中更新消息,在这种情况下是ngAfterContentChecked而不是ngAfterViewInit ,因为在ngAfterViewInit中检查variables消息已经启动,但尚未结束。

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

所以代码将只是:

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

请参阅Plunker的工作演示 。

它会抛出一个错误,因为当调用ngAfterViewInit()的时候你的代码被更新了。 意思是当ngAfterViewInit发生时你的初始值被改变了,如果你在ngAfterContentInit()中调用它,那么它不会抛出错误。

 ngAfterContentInit() { this.updateMessage(); } 

ngAfterViewChecked()为我工作:

 import { Component, ChangeDetectorRef } from '@angular/core'; //import ChangeDetectorRef constructor(private cdr: ChangeDetectorRef) { } ngAfterViewChecked(){ //your code to update the model this.cdr.detectchanges } 

您也可以使用rxjs Observable.timer创build一个计时器,然后更新订阅中的消息=>

Observable.timer(1).subscribe(()=> this.updateMessage());