请参阅以下Stackblitz:https://stackblitz.com/edit/angular-psqzbo?file=src%2Fapp%2Fhello.component.ts
请注意,width
成员有两种绑定:一种在模板中,另一种是主机绑定。主机绑定已被注释掉。请注意,没有引发ExpressionChanged错误-这是因为在我们更新this.cdr.detectChanges()
中的width
之后,正在调用ngAfterViewInit
。
现在取消注释主机绑定。观察到抛出了ExpressionChanged错误。为什么?是什么使这些绑定不同?这是错误吗?
编辑:这不是链接问题的重复。我知道,为什么这里需要detectChanges
,我的问题是为什么它在主机绑定上不起作用。请重新阅读。
答案 0 :(得分:1)
发生此错误是因为您所做的更改使先前呈现的组件视图无效。
Angular的单向数据流规则禁止在组合视图后对其进行更新。组成组件的视图之后,这两个钩子都会触发。
这对您的情况意味着什么:
当Angular第一次渲染/合成该组件的视图时(在ngAfterViewInit
之前),该元素的宽度设置为初始值。这段代码更改了元素的宽度,从而导致了视图的改变。
我认为this example使用背景色使这一点更加明显。在渲染视图的第一遍中,颜色为red
(您可以在屏幕上短暂看到它)。根据您的情况,在第一次通过时宽度绑定是不确定的。
然后,ngAfterViewInit
导致更改,使先前创建的视图无效,更改宽度或颜色,并触发错误。我将这个错误翻译为Angular说:“我做了很多工作,得出的观点很完美,然后您做了使该工作毫无价值的工作。您不应该这样做,因为它会中断我已有的一些性能优化/假设。”
可以通过使用ngAfterViewInit
确保在setTimeout
方法运行完成后进行组件更改来解决此问题。 Fixed example或通过将视图移到ngOnInit
来确保在呈现视图之前发生组件更改。
您可能会注意到,我创建的示例中没有detectChanges
。这是有意的,因为这是一个红色鲱鱼,实际上与问题无关(尽管您正确地指出,这对于模板绑定起作用是必需的)。在您的示例中注释掉了主机绑定后,就没有表达问题,因为this.width
对组件的呈现视图没有任何影响。
绑定在模板中时没有问题,因为这会导致更改组件的内容-而不是组件自己的视图。
我不完全了解的猜测/事情:
我认为这种行为归结为Shadow DOM与Light DOM的变化(我根据this SO question的信息进行猜测)。在ngAfterViewInit
运行时,我认为Light DOM中已经有一个空标签<my-component></my-component>
(IE实际上在页面上)。更改子内容不会引起问题,因为此时它在页面上实际上不存在,并且是Shadow DOM的一部分。但是,更改主机绑定会触发页面(Light DOM的一部分)上的 real 元素的更改-因此出现错误。