2

I cannot listen to an event multiple times on a single element.

I have noted following :

  • This issue occurs only when firing an RxJS observable from a lifecycle methods like ngAfterViewChecked, ngDoCheck or from the new afterRender() hook.
  • When listening to different events click & mousedown, there is no issue
  • This is likely related to the zone.js patching because there is no issue when providing ɵNoopNgZone.

I really don't understand what is happening. Any idea ?

Repro:

export class App implements AfterViewChecked {
  @ViewChild("button") button!: ElementRef<HTMLElement>;
  render$$ = new Subject<void>();

  ngAfterViewInit() {
    this.render$$
      .pipe(switchMap(() => fromEvent(this.button.nativeElement, "click")))
      .subscribe(() => console.log("--- click 1 ---"));
    this.render$$
      .pipe(switchMap(() => fromEvent(this.button.nativeElement, "click")))
      .subscribe(() => console.log("--- click 2 ---"));
  }

  ngAfterViewChecked() {
    this.render$$.next();
  }
}

Stackblitz repro

In this example --- click 2 --- is never logged despite listening to the same event/button.

Matthieu Riegler
  • 31,918
  • 20
  • 95
  • 134

1 Answers1

1

This works "as intented" and I have found why this happens :

Angular evaluates its event listeners in the Angular zone, and each individual NgZone.run call triggers change detection. Therefore, the render$$ subject emits, causing the switchMap to unsubscribe from the event listener that is still pending, effectively cancelling the event.

This problem can be avoided by turning on event coalescing, such that change detection only runs after all event listeners have been invoked.

So the fix is :

bootstrapApplication(
  App,
  {
    providers: [provideZoneChangeDetection({ eventCoalescing: true })],
  }
);
Matthieu Riegler
  • 31,918
  • 20
  • 95
  • 134