Angular @HostListener event triggered multiple times

Viewed 2504

I'm building an Ionic 3 app, where I have made an "auto-complete" directive, which is a simple directive that pops up an auto-complete dialog when the element gets focus. This is done in "auto-complete" via:

@HostListener('ionFocus', ['$event._native.nativeElement'])
  onFocus(target) {
      target.blur();
      this.showAutocompleteDialog(target);
  }

This worked when I only used the directive in pages that were only used once in the system.

But then I made a new component, let's call it "AddressField", which has a "auto-complete" directive in one of the elements in its template. Now the @HostListener onFocus event gets triggered twice and hence two autocomplete dialogs are shown.

This only happens for the "auto-complete" directives within the AddressField components, and it only triggers twice for the second instance of "AddressField" in the system.

Am I somehow using the @HostListener in a wrong way, or does this sound like a bug that should be filed? In either case, a workaround of some sort would be really helpful.

Thanks

2 Answers

Just an update on this:

In the end, it's my impression that this comes down to a bug in angular (see fx https://github.com/ionic-team/ionic-v3/issues/86) - at least, I wasn't able to find any problems with my approach.

My solution was a hacky one: I added a flag "alreadyFired" to my directive, checking for that in my HostListener function.

@HostListener('ionFocus', ['$event._native.nativeElement'])
  onFocus(target) {
    if (this.alreadyFired) {
      return;
    }
    this.alreadyFired = true;
    target.blur();
    this.showAutocompleteDialog(target);
}

And then of course when dismissing the dialog, I again set the alreadyFired flag to false again. Hacky, but working under a deadline..

I had a very similar problem where I had two instances of the same component. Therefore there were two EventListeners.

The solution to this was adding the EventListener the oldschool way like this:

if (!condition) {
        window.addEventListener('keydown', (event) => {
            //do something
        });
    }

In one of the components I set an attribute so the condition would apply. This was done by an

@Input()
condition = false;

and in one of the html elements

[condition]="true"

In the end only one of the components fired the event.

Note: It is important that these EventListeners also get closed again to prevent memory leaks. This can be done for instance in the ngOnDestroy() hook.

Related