Search code examples
angularangular-changedetection

Prevent Angular HostListener propagation to parent component


Issue

One of my components has a HostListener binding set up:

@HostListener('window:resize', ['$event']) callback ($event) {
   // D3Js tasks here …
}

Currently, when the listener gets called, the parent component re-evaluate its template as if the parent did respond to some child's output.
But performed tasks from the listener do not need any update from the parent component.

I wish the listener could execute without causing the parent component to be kind of notified.

Can such bubbling behavior be prevented somewhere in Angular in this context?

Attempts

Neither $event.stopPropagation() nore $event.stopImmediatePropagation() did help here, probably because this behavior is tied to Angular internals.
Leaving the HostListener declaration empty (commenting out contained instructions) does not change anything which in my opinion indicates the HostListener declaration is the culprit here.

My current solution is to directly create an observable out of the native DOM event (without relying on Angular), within the component constructor:

this.onResizeSub = Observable
    .fromEvent(window, 'resize', null, null)
    .subscribe( () => {…});
);

Notes: using Angular v4.4.2, change detection OnPush strategy all over the place.


Solution

  • Unfortunately it seems this is currently not supported in Angular. There's a feature request but it looks like we're not going to see this any time soon: https://github.com/angular/angular/issues/9587, https://github.com/angular/angular/issues/10990

    The order of execution is red herring - the order in which an event on the same element is propagated to multiple listeners is currently undefined. this is currently by design.

    Your problem is that the event exposed to the listeners is the real DOM event and calling stopImmediatePropagation() on the provided event stops execution of other listeners registered on this element. However since all the the listeners registered via Angular are proxied by just a single dom listener (for performance reasons) calling stopImmediatePropagation on this event has no effect.