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如何解决它:
-
使用
setTimeout
,Promise.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());