javascript - 即使在调用 detectChanges 时,主机绑定也会引发 ExpressionChanged 错误
问题描述
请参阅以下 Stackblitz:https ://stackblitz.com/edit/angular-psqzbo?file=src%2Fapp%2Fhello.component.ts
请注意,该成员有两个绑定width
:一个在模板中,另一个是主机绑定。主机绑定现在被注释掉了。请注意,没有抛出 ExpressionChanged 错误——这是因为在this.cdr.detectChanges()
我们更新.width
ngAfterViewInit
现在取消注释主机绑定。观察是否引发了 ExpressionChanged 错误。为什么?这些绑定有何不同?这是一个错误吗?
编辑:这不是链接问题的欺骗。我知道这里为什么detectChanges
需要,我的问题是为什么它不能在主机绑定上工作。请重读。
解决方案
发生此错误是因为您所做的更改会使先前呈现的组件视图无效。
引用本文档
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 的一部分)的更改 - 因此会出现错误。
推荐阅读
- c# - C# XML 读取和编辑子值
- excel - 如何比较 A/B 列,并从 A 列返回与 B 不匹配的值
- c# - 使用 513 个空格字符在 C# 中将文本写入文件
- vuejs2 - Vue 2.5 - Slots / Slot scope - 将库组件封装成自定义组件
- animation - 仅 iOS 上的 Animation.css 错误
- sharepoint - SharePoint Online 中心网站导航 - 没有超链接的顶级菜单项
- c# - 从 TimeZoneInfo 获取夏令时信息
- mysql - 字符集转换 SQL
- css - 弹出框适合 Chrome 中的内容宽度,但不适合 Edge
- c# - C# JSON 解析而不是 R