首页 > 解决方案 > 即使在调用 detectChanges 时,主机绑定也会引发 ExpressionChanged 错误

问题描述

请参阅以下 Stackblitz:https ://stackblitz.com/edit/angular-psqzbo?file=src%2Fapp%2Fhello.component.ts

请注意,该成员有两个绑定width:一个在模板中,另一个是主机绑定。主机绑定现在被注释掉了。请注意,没有抛出 ExpressionChanged 错误——这是因为在this.cdr.detectChanges()我们更新.widthngAfterViewInit

现在取消注释主机绑定。观察是否引发了 ExpressionChanged 错误。为什么?这些绑定有何不同?这是一个错误吗?

编辑:这不是链接问题的欺骗。我知道这里为什么detectChanges需要,我的问题是为什么它不能在主机绑定上工作。请重读。

标签: javascriptangular

解决方案


发生此错误是因为您所做的更改会使先前呈现的组件视图无效。

引用本文档

Angular 的单向数据流规则禁止在视图组合后对其进行更新。这两个钩子都会在组件的视图合成后触发。

这对您的情况意味着什么:

当 Angular 首次渲染/组合该组件的视图时(prior ngAfterViewInit),元素的宽度被设置为初始值。此代码更改元素的宽度,从而导致视图发生更改。

我认为这个使用背景颜色的例子使这一点更加明显。在渲染视图的第一遍中,颜色是red(您可以在屏幕上简单地看到它)。在您的情况下,第一次通过时未定义宽度绑定。

然后,ngAfterViewInit导致先前创建的视图无效的更改,更改宽度或颜色,触发错误。我将此错误翻译为 Angular 说“我做了很多工作并做出了一个完美的视图,然后你做了一些让这项工作毫无价值的事情。你不应该这样做,因为它会打断我的一些性能优化/假设” .

这可以通过确保在ngAfterViewInit方法完成运行后发生组件更改来解决,方法是使用setTimeout. 固定示例或者通过将组件移动到ngOnInit.


您可能会注意到detectChanges我创建的示例中没有。这是故意的,因为这是一个红鲱鱼,实际上与问题无关(尽管您正确地声明这是模板绑定工作所必需的)。在您的示例中注释掉主机绑定时,没有表达式问题,因为this.width对组件的呈现视图没有影响

当绑定在模板中时没有问题,因为这会导致组件内容的更改 - 而不是组件自己的视图。


推测/我不完全理解的事情:

我相信这种行为归结为 Shadow DOM 与 Light DOM 的变化(我根据这个 SO question的信息进行猜测)。在ngAfterViewInit运行的时候,我相信Light DOM中已经有一个空标签<my-component></my-component>(IE实际上在页面上)。更改子内容不会导致问题,因为此时它实际上并不存在于页面上并且是 Shadow DOM 的一部分。但是,更改主机绑定会触发对页面上真实元素(Light DOM 的一部分)的更改 - 因此会出现错误。


推荐阅读