Dan*_*cák 5 javascript angular angular-changedetection
简而言之,我的问题是组件模板中有一个元素。该元素有一个ngIf
条件和一个(click)
处理程序。它不会从一开始就渲染,因为 ngIf 条件的计算结果为false
。
现在有趣的部分来了:在角度区域之外运行的代码将该条件更改为true
,并且在detectChanges
手动执行更改检测器引用后,该元素将被渲染,并且单击处理程序 ofc 将变为活动状态。
到目前为止,一切似乎都正常,但问题是,当(click)
用户单击回调时,不会触发组件的更改检测。
这是复制品https://stackblitz.com/edit/angular-kea4wi
在那里重现它的步骤:
描述:
米色区域有一个通过 addEventListener 注册的点击事件处理程序,并且该事件监听器的回调在角度区域之外运行。在其中,组件的showButton
属性设置为从false
到true
,我通过调用 手动触发更改检测detectChanges()
,否则属性中的更改showButton
将不会被注册。代码如下所示:
this.zone.runOutsideAngular(() => {
const el = this.eventTarget.nativeElement as HTMLElement;
el.addEventListener('click', e => {
this.showButton = true;
this.cd.detectChanges();
})
})
Run Code Online (Sandbox Code Playgroud)现在按钮出现了,这要归功于*ngIf="showButton"
最初没有渲染,并且它在模板中声明了一个单击事件处理程序。该处理程序再次更改组件的属性,这次showMessage
更改为true
.
<button *ngIf="showButton" (click)="onButtonClick()">Click me!</button>
onButtonClick() {
this.showMessage = true;
}
Run Code Online (Sandbox Code Playgroud)当我单击它时,处理程序显然会运行并将组件更改showMessage
为true
,但它不会触发更改检测,并且不会出现下面的消息。要使示例正常工作,只需从一开始就将 showButton 设置为 true,上面的场景就可以工作。
问题是:这怎么可能?由于我(click)
在模板中声明了事件处理程序,它在调用时不应该总是触发更改检测吗?
我在 Angular 的存储库中创建了一个问题,事实证明,这种行为是合乎逻辑的,尽管可能是出乎意料的。重新表述 Angular 团队所写的内容:
导致带有处理程序的元素渲染的代码(click)
在问题中所述的角度区域之外运行。现在,虽然我detectChanges()
在那里手动执行,但这并不意味着代码突然神奇地在角度区域中运行。它可以正常运行变化检测,但它“停留”在不同的区域。因此,当元素即将被渲染时,元素的单击回调将在non-angular zone 中创建并绑定到 non-angular zone。这反过来意味着当用户点击触发它时,它仍然被调用,但不会触发更改检测。
解决方案是将代码包装在角度区域之外,但需要在组件中执行一些更改zone.run(() => {...})
.
因此,在我的 stackblitz 复制中,在角度区域之外运行的代码将如下所示:
this.zone.runOutsideAngular(() => {
const el = this.eventTarget.nativeElement as HTMLElement;
el.addEventListener('click', e => {
this.zone.run(() => this.showButton = true);
})
})
Run Code Online (Sandbox Code Playgroud)
与调用不同的是,这detectChanges()
使得this.showButton = true
在正确的区域中运行,因此通过事件处理程序运行该代码而创建的元素也绑定到角度区域。这样,事件处理程序总是触发更改检测。
这一切都归结为以下要点:在模板中声明事件处理程序 并不能自动保证在所有场景中都能检测到更改。
归档时间: |
|
查看次数: |
5642 次 |
最近记录: |