Search code examples
angulartypescriptevents

Emitting Angular events overlapping with HTML DOM Events


I had a working code emitting an event like so.

@Output() actChange = new EventEmitter<string[]>();
private emitIds() { ... 
  this.actChange.emit(includedIds); 
}

It was successfully handling the event like this.

<app-child (actChange)="onActChange($event)">

onActChange(input: string[]) {
  console.log('input', input);
  ...
}

Then, I changed it by removing the indicator of acts ending up with this.

@Output() change = new EventEmitter<string[]>();
private emitIds() { ... 
  this.change.emit(includedIds); 
}

<app-child (change)="onChange($event)">

onChange(input: string[]) {
  console.log('input', input);
  ...
}

It still works as supposed to, with a single differece. The method is now invoked twice: once for the previously desired effect, then, once again, for the default event of type Event (the default change of something in the HTML).

I understand what happens and why. However, I have multiple ways to approach it and I'm not certain which to pick. I fear the unexpected, currently obscure, negative implications.

Option 1 is not to use overlapping names for the event.

@Output() actChange = new EventEmitter<string[]>();

Option 2 is to handle the default change by explictly guarding against it.

onChange(input: string[]) {
  console.log('input', input);
  if (input instanceof Event)
    return;
  ...
}

Option 3 would be to turn off the default event being fired (unclear how to, though).

I've googled it and read up on emitting events, suggested style etc. without finding any clear indication of reusing an event's name nor how to deal with it. I've seen a suggestion to prefix but it wasn't a canonical best-practice.

Which of the options above carry zero (or minimal) nagative implications? What other approach would minimize the risks in the future?


Solution

  • TLDR; Option 1 would be the way.


    1. Option #1: Naming convention using named action prefix, ex: actChange

    Never use "on" as a prefix.

    Here there's an unofficial blog post reference as a best practice. However, you may can consider it as is, because angular/angular-cli codelyzer project added it as a parameter called no-output-named-after-standard-event to handle it from tslint.

    Ensures that output bindings, including aliases, are not named as standard DOM events

    The rule got migrated to eslint as no-output-native: enter image description here

    An implicit naming convention regarding that feature, would be to name your events as for WHEN they're triggered. In your case, the event is triggered when your "act" is changed, not your whole component.


    1. Option #2: Handling change inside implementation

    This option falls into the same pitfall being addressed by codelyzer about Events. Don't name them as any HTML DOM Events.

    Also, this approach comes with potencial bugs, as developers have to remember to handle HTML DOM events.


    1. Option #3: Remove HTML DOM event

    The change event is not cancellable, so forget about this.


    Some notes: I think there's no official documentation regarding this matter, because they just consider it as an implicit rule:

    • Even angular/components project, straight forward refused to handle the issue.
    • Also, there was a feature request at angular/angular project to remove the extra event. However, it got old and then closed automatically.