为什么这个组件在这个简单的插件中
@Component({ selector: 'my-app', template: `I'm {{message}}`, }) export class App { message:string = 'loading :('; ngAfterViewInit() { this.updateMessage(); } updateMessage(){ this.message = 'all done loading :)' } }
投掷:
例外:App @ 0:5'中的表达式'我'{{message}}在检查后发生了变化.上一个值:'我正在加载:('.当前值:'我已经完成了加载:)'在[我是{{message}}在App @ 0:5中]
什么时候我正在做的是在我的视图启动时更新一个简单的绑定?
正如drewmoore所述,在这种情况下,正确的解决方案是手动触发当前组件的变化检测.这是使用对象的detectChanges()
方法ChangeDetectorRef
(从中导入angular2/core
)或其markForCheck()
方法完成的,该方法还使任何父组件更新.相关例子:
import { Component, ChangeDetectorRef, AfterViewInit } from 'angular2/core' @Component({ selector: 'my-app', template: `I'm {{message}}`, }) export class App implements AfterViewInit { message: string = 'loading :('; constructor(private cdr: ChangeDetectorRef) {} ngAfterViewInit() { this.message = 'all done loading :)' this.cdr.detectChanges(); } }
这里同时Plunkers展示ngOnInit,setTimeout的,并且enableProdMode以防万一的方法.
首先,请注意,只有当您以开发模式运行应用程序时才会抛出此异常(默认情况下属于beta-0):如果您enableProdMode()
在引导应用程序时调用它,则不会抛出它(请参阅更新了plunk).
其次,不要这样做是因为这个例外被抛出是有充分理由的:简而言之,当处于开发模式时,每一轮变更检测都会立即进行第二轮检查,以确认自第一轮结束后没有任何绑定发生变化,因为这表明变化是由变化检测本身引起的.
在你的插件中,绑定{{message}}
会被你的调用改变setMessage()
,这发生在ngAfterViewInit
钩子中,这是初始变化检测转弯的一部分.这本身并不成问题 - 问题在于setMessage()
更改绑定但不会触发新一轮的更改检测,这意味着直到某个未来的一轮更改检测在其他地方触发时才会检测到此更改.
要点:任何改变绑定的东西都需要触发一轮变化检测.
更新以响应所有请求以获取如何执行此操作的示例:@Tycho 的解决方案正常工作,@ MarkRajcok指出的答案中的三种方法也是如此.但坦率地说,他们都觉得我很难看,就像我们习惯于在ng1中依赖的那种黑客一样.
可以肯定的是,偶尔存在这些黑客攻击的情况,但是如果你在非常偶然的基础上使用它们,那就表明你正在与框架作斗争,而不是完全接受它的反应性质.
恕我直言,一个更惯用的,"Angular2方式"接近这一点是这样的:( plunk)
@Component({ selector: 'my-app', template: `I'm {{message | async}}` }) export class App { message:Subject= new BehaviorSubject('loading :('); ngAfterViewInit() { this.message.next('all done loading :)') } }
我通过从angular core添加ChangeDetectionStrategy来解决这个问题.
import { Component, ChangeDetectionStrategy } from '@angular/core'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, selector: 'page1', templateUrl: 'page1.html', })
你不能使用,ngOnInit
因为你只是改变成员变量message
?
如果要访问子组件的引用@ViewChild(ChildComponent)
,确实需要等待它ngAfterViewInit
.
一个脏修复是updateMessage()
使用例如setTimeout 调用下一个事件循环.
ngAfterViewInit() { setTimeout(() => { this.updateMessage(); }, 1); }
ngAfterViewChecked()为我工作:
import { Component, ChangeDetectorRef } from '@angular/core'; //import ChangeDetectorRef constructor(private cdr: ChangeDetectorRef) { } ngAfterViewChecked(){ //your code to update the model this.cdr.detectChanges(); }
文章关于ExpressionChangedAfterItHasBeenCheckedError
错误需要了解的所有内容都会详细解释行为.
您设置的问题ngAfterViewInit
是在更改检测处理DOM更新后执行生命周期钩子.并且您正在有效地更改此挂钩中模板中使用的属性,这意味着需要重新呈现DOM:
ngAfterViewInit() { this.message = 'all done loading :)'; // needs to be rendered the DOM }
这将需要另一个更改检测周期,而Angular by design只运行一个摘要周期.
你基本上有两个选择如何解决它:
异步更新属性或者使用setTimeout
,Promise.then
或在模板中引用异步观察到
在DOM更新之前在钩子中执行属性更新 - ngOnInit,ngDoCheck,ngAfterContentInit,ngAfterContentChecked.
为此,我已经尝试了以上答案,但许多版本在Angular的最新版本(6或更高版本)中均不起作用
我正在使用Material控件,该控件在完成第一次绑定后需要进行更改。
export class AbcClass implements OnInit, AfterContentChecked{ constructor(private ref: ChangeDetectorRef) {} ngOnInit(){ // your tasks } ngAfterViewInit() { this.ref.detectChanges(); } }
因此,请添加我的答案,这有助于解决某些特定问题。
您只需要在正确的生命周期钩子中更新您的消息,在这种情况下,ngAfterContentChecked
而不是ngAfterViewInit
,因为在ngAfterViewInit 中,已经启动了对变量消息的检查但尚未结束.
请参阅:https: //angular.io/docs/ts/latest/guide/lifecycle-hooks.html#!#afterview
所以代码将是:
import { Component } from 'angular2/core' @Component({ selector: 'my-app', template: `I'm {{message}}`, }) export class App { message: string = 'loading :('; ngAfterContentChecked() { this.message = 'all done loading :)' } }
看看Plunker 的工作演示.