主机绑定即使在调用detectChanges时也会引发ExpressionChanged错误

时间:2019-04-05 01:34:14

标签: javascript angular

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

请注意,width成员有两种绑定:一种在模板中,另一种是主机绑定。主机绑定已被注释掉。请注意,没有引发ExpressionChanged错误-这是因为在我们更新this.cdr.detectChanges()中的width之后,正在调用ngAfterViewInit

现在取消注释主机绑定。观察到抛出了ExpressionChanged错误。为什么?是什么使这些绑定不同?这是错误吗?

编辑:这不是链接问题的重复。我知道,为什么这里需要detectChanges,我的问题是为什么它在主机绑定上不起作用。请重新阅读。

1 个答案:

答案 0 :(得分:1)

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

引用this documentation

  

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 元素的更改-因此出现错误。