Angular 单击事件处理程序未触发更改检测

Angular click event handler not triggering change detection

简而言之,我在组件模板中有一个元素。该元素有一个 ngIf 条件和一个 (click) 处理程序。它不是从一开始就呈现的,因为 ngIf 条件的计算结果为 false.

现在有趣的部分来了:在 angular 区域 运行ning 之外的代码将该条件更改为 true,并且在执行之后detectChanges 手动更改检测器引用,此元素被渲染并且点击处理程序 ofc 变为活动状态。

目前看来一切正常,但问题是当用户点击 (click) 回调 运行 时, 未触发更改检测对于组件。

这里是复制品https://stackblitz.com/edit/angular-kea4wi

在那里复制它的步骤:

  1. 点击米色区域
  2. 按钮出现,也点击它
  3. 没有任何反应,尽管消息应该出现在下方

描述:

  1. 米色区域有一个通过addEventListener注册的点击事件处理器,这个事件监听器的回调是运行宁在angular区外.在其中,组件的 showButton 属性 设置为 falsetrue,我通过调用 detectChanges() 手动触发更改检测,否则 showButton 属性 不会被注册。代码如下所示:

    this.zone.runOutsideAngular(() => {
       const el = this.eventTarget.nativeElement as HTMLElement;
       el.addEventListener('click', e => {
         this.showButton = true;
         this.cd.detectChanges();
       })
     })
    
  2. 现在出现了按钮,这要归功于 *ngIf="showButton" 最初没有呈现,并且它有一个在模板中声明的点击事件处理程序。此处理程序再次更改组件的 属性,这次 showMessage 更改为 true

    <button *ngIf="showButton" (click)="onButtonClick()">Click me!</button>
    
    onButtonClick() {
      this.showMessage = true;
    }
    
  3. 当我点击它时,处理程序显然 运行s 并将组件的 showMessage 更改为 true,但它 不会触发更改检测 和下面的消息不会出现。 要使示例运行,只需从一开始就将 showButton 设置为 true,上面的场景就可以运行。

问题是:这怎么可能?由于我在模板中声明了 (click) 事件处理程序,它不应该在调用时始终触发更改检测吗?

我在 Angular 的 repo 中创建了 an issue,事实证明,这种 行为是合乎逻辑的 ,尽管可能出乎意料。改写 Angular 团队在那里写的内容:

如问题中所述,导致具有 (click) 处理程序的元素呈现的代码是 运行ning 在 Angular 区域之外。现在,虽然我在那里手动执行 detectChanges(),但这并不意味着 angular 区域中的代码突然神奇地 运行s。它 运行 变化检测没问题,但它“保持”在不同的区域。因此,当元素即将被渲染时,元素的点击回调被创建并绑定到非angular区域。这反过来意味着当它被用户点击触发时,它仍然被调用,但不会触发变化检测

解决方案是包装代码,运行在 angular 区域之外,但需要在组件中执行一些更改,在 zone.run(() => {...}).[=17= 中]

所以在我的 stackblitz 复制中,angular 区域外的代码 运行ning 看起来像这样:

    this.zone.runOutsideAngular(() => {
      const el = this.eventTarget.nativeElement as HTMLElement;
      el.addEventListener('click', e => {
        this.zone.run(() => this.showButton = true);
      })      
    })

这与调用 detectChanges() 不同,它使 this.showButton = true 运行 在正确的区域中,因此 运行 宁该代码的结果也创建了元素他们的事件处理程序绑定到 angular 区域。这样,事件处理程序在对 DOM 事件做出反应时总是 触发更改检测

这一切都归结为以下要点:在模板中声明事件处理程序并不能自动保证在所有情况下都能检测到变化 .