更新 AfterViewInit 中的布尔值会导致 "Expression has changed after it was checked"
Updating boolean in AfterViewInit causes "Expression has changed after it was checked"
我有一个在视图中动态创建的简单警报组件。由于它是动态创建的,因此我设置了一个选项以在初始化后自动显示警报。
虽然它在工作,但我想了解为什么在这种特殊情况下我必须手动触发更改检测。
代码:
export class OverlayMessageComponent implements AfterViewInit {
...
ngAfterViewInit() {
if(this.autoShow) {
this.show();
}
this.changeDetector.detectChanges();
}
...
}
完整样本: https://plnkr.co/edit/8NvfhDvLVBd71I7DR0kW
我必须添加 this.changeDetector.detectChanges();
,因为我收到以下错误:
EXCEPTION: Expression has changed after it was checked.
我的印象是使用 AfterViewInit
有助于避免这个问题,但我认为我的假设是错误的。有没有更好的结构代码来避免这个错误?
我想更好地理解返回此错误的原因。我以前见过这个错误几次,我知道有人说 setTimeout()
或 enableProdMode()
确实解决了这个问题,但对我来说,当框架本身通知时,这似乎是一个 hacky 解决方法你说有问题。
当您在 angular 之后更新模型时使用 detectChanges()
运行 它是变化检测,或者如果更新尚未在 angular 世界中全部.
I'd like to understand better why this error is returned
AfterViewInit
和 AfterViewChecked
生命周期挂钩在更改检测完成并构建视图后触发。因此,此时 运行s 的任何代码都不应更新视图,否则您的应用及其视图将不同步。拍一张look at the docs
Angular's unidirectional data flow rule forbids updates to the view after it has been composed. Both of these hooks fire after the component's view has been composed.
Angular throws an error if the hook updates the component's data-bound comment property immediately.
因此,您必须手动触发更改检测——这是一项昂贵的操作,因为 Angular 必须再次检查整个应用程序——或者异步进行更改,以便视图在下一个更改检测步骤更新,例如:
if(this.autoShow) { setTimeout(()=>this.show,0)}
或者更简单地说,如果您不需要在视图中获取某些内容的句柄,您可以 运行 在 ngOnInit()
中或稍后在 ngAfterContentInit()
中编写代码.因为这些 运行 在视图组合之前,您可以毫不费力地进行影响视图的更改。
修复
对于您的特定情况,无需触发更改检测或使用异步更新。修复很简单,只需将 this.show
移动到 ngOnInit
生命周期挂钩:
ngOnInit() {
if(this.autoShow) {
this.show();
}
}
解释
因为您在模板绑定中使用 bringIconToFront
组件 属性:
<div class="icon home" [class.add-z-index]="bringIconToFront"></div>
Angular 应该更新 App
组件的 DOM。此外,Angular 为子 OverlayMessage
组件调用生命周期挂钩。 DOM 更新和生命周期挂钩按顺序执行 here:
- 在子组件上调用
OnInit
和 ngDoCheck
(OnInit
仅在第一次检查时调用)
- 更新 DOM 当前
App
视图的插值和绑定(如果当前视图组件实例上的属性发生更改)`
- 为子
OverlayMessage
组件调用 ngAfterViewInit
和 ngAfterViewChecked
- 为当前
App
组件调用 ngAfterViewInit
和 ngAfterViewChecked
您可以看到在为当前组件更新 DOM 绑定之前调用了 onInit
。然后 ngAfterViewInit
被调用。这就是为什么它在一种情况下有效而在另一种情况下无效的原因。
本文将帮助您更好地理解错误 -
Everything you need to know about the ExpressionChangedAfterItHasBeenCheckedError
error.
我有一个在视图中动态创建的简单警报组件。由于它是动态创建的,因此我设置了一个选项以在初始化后自动显示警报。
虽然它在工作,但我想了解为什么在这种特殊情况下我必须手动触发更改检测。
代码:
export class OverlayMessageComponent implements AfterViewInit {
...
ngAfterViewInit() {
if(this.autoShow) {
this.show();
}
this.changeDetector.detectChanges();
}
...
}
完整样本: https://plnkr.co/edit/8NvfhDvLVBd71I7DR0kW
我必须添加 this.changeDetector.detectChanges();
,因为我收到以下错误:
EXCEPTION: Expression has changed after it was checked.
我的印象是使用 AfterViewInit
有助于避免这个问题,但我认为我的假设是错误的。有没有更好的结构代码来避免这个错误?
我想更好地理解返回此错误的原因。我以前见过这个错误几次,我知道有人说 setTimeout()
或 enableProdMode()
确实解决了这个问题,但对我来说,当框架本身通知时,这似乎是一个 hacky 解决方法你说有问题。
当您在 angular 之后更新模型时使用 detectChanges()
运行 它是变化检测,或者如果更新尚未在 angular 世界中全部.
I'd like to understand better why this error is returned
AfterViewInit
和 AfterViewChecked
生命周期挂钩在更改检测完成并构建视图后触发。因此,此时 运行s 的任何代码都不应更新视图,否则您的应用及其视图将不同步。拍一张look at the docs
Angular's unidirectional data flow rule forbids updates to the view after it has been composed. Both of these hooks fire after the component's view has been composed.
Angular throws an error if the hook updates the component's data-bound comment property immediately.
因此,您必须手动触发更改检测——这是一项昂贵的操作,因为 Angular 必须再次检查整个应用程序——或者异步进行更改,以便视图在下一个更改检测步骤更新,例如:
if(this.autoShow) { setTimeout(()=>this.show,0)}
或者更简单地说,如果您不需要在视图中获取某些内容的句柄,您可以 运行 在 ngOnInit()
中或稍后在 ngAfterContentInit()
中编写代码.因为这些 运行 在视图组合之前,您可以毫不费力地进行影响视图的更改。
修复
对于您的特定情况,无需触发更改检测或使用异步更新。修复很简单,只需将 this.show
移动到 ngOnInit
生命周期挂钩:
ngOnInit() {
if(this.autoShow) {
this.show();
}
}
解释
因为您在模板绑定中使用 bringIconToFront
组件 属性:
<div class="icon home" [class.add-z-index]="bringIconToFront"></div>
Angular 应该更新 App
组件的 DOM。此外,Angular 为子 OverlayMessage
组件调用生命周期挂钩。 DOM 更新和生命周期挂钩按顺序执行 here:
- 在子组件上调用
OnInit
和ngDoCheck
(OnInit
仅在第一次检查时调用) - 更新 DOM 当前
App
视图的插值和绑定(如果当前视图组件实例上的属性发生更改)` - 为子
OverlayMessage
组件调用ngAfterViewInit
和ngAfterViewChecked
- 为当前
App
组件调用ngAfterViewInit
和ngAfterViewChecked
您可以看到在为当前组件更新 DOM 绑定之前调用了 onInit
。然后 ngAfterViewInit
被调用。这就是为什么它在一种情况下有效而在另一种情况下无效的原因。
本文将帮助您更好地理解错误 -
Everything you need to know about the ExpressionChangedAfterItHasBeenCheckedError
error.