在 AfterViewInit 中更新布尔值会导致“检查后表达式已更改”
Posted
技术标签:
【中文标题】在 AfterViewInit 中更新布尔值会导致“检查后表达式已更改”【英文标题】:Updating boolean in AfterViewInit causes "Expression has changed after it was checked" 【发布时间】:2017-12-14 04:38:47 【问题描述】:我有一个在视图中动态创建的简单警报组件。由于它是动态创建的,因此我设置了一个选项以在初始化后自动显示警报。
虽然它正在工作,但我想了解为什么在这种特殊情况下我必须手动触发更改检测。
代码:
export class OverlayMessageComponent implements AfterViewInit
...
ngAfterViewInit()
if(this.autoShow)
this.show();
this.changeDetector.detectChanges();
...
完整示例: https://plnkr.co/edit/8NvfhDvLVBd71I7DR0kW
我必须添加this.changeDetector.detectChanges();
,因为我收到以下错误:
异常:表达式在检查后发生了变化。
我的印象是使用AfterViewInit
有助于避免该问题,但我认为我的假设是错误的。有没有办法更好地构造代码以避免此错误?
我想更好地了解为什么会返回此错误。我以前见过这个错误几次,我知道有人说setTimeout()
或enableProdMode()
确实解决了这个问题,但对我来说,当框架本身通知你有一个问题。
【问题讨论】:
这将有助于从这里了解detectChanges
***.com/a/41364469/8171406
本文将帮助您更好地理解错误-Everything you need to know about the ExpressionChangedAfterItHasBeenCheckedError
error。
【参考方案1】:
我想更好地理解为什么会返回这个错误
更改检测完成并构建视图后,将触发 AfterViewInit
和 AfterViewChecked
生命周期挂钩。因此,此时运行的任何代码都不应更新视图,否则您的应用程序及其视图将不同步。采取look at the docs
Angular 的单向数据流规则禁止在视图组合后对其进行更新。这两个钩子都会在组件的视图合成后触发。
如果钩子立即更新组件的数据绑定评论属性,Angular 会抛出错误。
因此,您必须手动触发更改检测——这是一项昂贵的操作,因为 Angular 必须再次遍历整个应用程序——或者异步进行更改,以便在下一次更改检测时更新视图步骤,例如:
if(this.autoShow) setTimeout(()=>this.show,0)
或者更简单地说,如果您不需要在视图中获取某些内容的句柄,您可以在ngOnInit()
或稍后在ngAfterContentInit()
中运行您的代码。因为它们在视图组合之前运行,所以您可以轻松地进行影响视图的更改。
文档:lifecycle hooks order
【讨论】:
@Maximus 你是什么意思? @Maximus 我还向底部建议了ngOnInit
。取决于用户是否需要获取视图中的某些内容。
@Maximus 我不知道它是否在那里工作,因为我们只有 OP 中的摘录
我添加了信息,如果您移至ngOnInit
,为什么它会起作用
这不会在setTimeout
触发后触发另一个changeDetection 吗?为什么它会比手动触发变更检测便宜?【参考方案2】:
修复
对于您的特定情况,无需触发更改检测或使用异步更新。修复很简单,只需将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
仅在第一次检查时调用)
如果当前视图组件实例上的属性发生更改,则更新当前 App
视图的 DOM 插值和绑定`
为子 OverlayMessage
组件调用 ngAfterViewInit
和 ngAfterViewChecked
为当前的App
组件调用ngAfterViewInit
和ngAfterViewChecked
您可以看到在为当前组件更新 DOM 绑定之前调用了 onInit
。然后调用ngAfterViewInit
。这就是为什么它在一种情况下有效而在另一种情况下无效的原因。
本文将帮助您更好地理解错误 -
Everything you need to know about the ExpressionChangedAfterItHasBeenCheckedError
error.
【讨论】:
感谢您的详细解答。这对我帮助很大! 不客气,您肯定会想阅读我在答案中提到的文章,以避免将来出现这些错误。我还参考了one of your answers下的一篇关于forwardRef
的文章
@DanielGrima,谢谢,请务必关注我以获得更深入的文章)【参考方案3】:
当您在 Angular 运行后更新模型时,使用 detectChanges()
进行更改检测,或者如果更新根本不在 Angular 世界中。
【讨论】:
以上是关于在 AfterViewInit 中更新布尔值会导致“检查后表达式已更改”的主要内容,如果未能解决你的问题,请参考以下文章
[DataGridCheckBoxColumn在属性更改时不会在MVVM中更新
为啥@ViewChild 不能在 AfterViewInit 之外运行? [复制]