Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Input vs Output Event Binding

I'm looking for an argument on why using @Output for events is better than passing an @Input function in Angular 2+.

Using @Input:

Parent Template:

<my-component [customEventFunction]=myFunction></my-component>

Inside parent-component.ts:

myFunction = () => {
  console.log("Hello world")
}

Inside my-component.ts

@Input() customEventFunction: Function;

someFunctionThatTriggersTheEvent() {
  this.customEventFunction();
}

Using @Output:

Parent Template:

<my-component (onCustomEvent)=myFunction()></my-component>

Inside parent-component.ts:

myFunction() {
  console.log("Hello world")
}

Inside my-component.ts

@Output() onCustomEvent: EventEmitter<any> = new EventEmitter<any>();

someFunctionThatTriggersTheEvent() {
  this.onCustomEvent.emit();
}

Both achieve the same goal, but I think that the @Output method is more typical from what I've seen in other Angular packages. One could argue that with an Input, you can check to see if the function exists if the event should only be triggered conditionally.

Thoughts?

like image 325
Ben Atlas Avatar asked Dec 19 '17 16:12

Ben Atlas


3 Answers

The advantages of @Output event binding:

  1. Defining an event with @Output makes it clear that it expects callback methods to handle the event, using the standard Angular mechanism and syntax.
  2. Many event handlers can subscribe to the @Ouptut event. On the other hand, if you define an @Input property that accepts a callback function, only one event handler can be registered; assigning a second event handler would disconnect the first one. To make a parallel with standard DOM event handlers, the @Input callback function binding is similar to setting onmousemove="doSomething()" while the @Output event binding is more like calling btn.addEventListener("mousemove", ...).
like image 190
ConnorsFan Avatar answered Oct 05 '22 12:10

ConnorsFan


@Sajeetharan's answer actually isn't quite correct: there is a significant functional difference: the execution context. Consider this scenario:

@Component({
  selector: 'app-example',
  template: `<button (click)="runFn()">Click Me</button>`,
})
export class ExampleComponent {
  @Input() public fn: any;

  public runFn(): void {
    this.fn();
  }
}

@Component({
  selector: 'app',
  template: `<app-example [fn]="myFn"></app-example>`,
})
export class AppComponent {
  public state = 42;

  // Using arrow syntax actually *will* alert "42" because
  // arrow functions do not have their own "this" context.
  //
  // public myFn = () => window.alert(this.state);

  public myFn(): void {
    // Oops, this will alert "undefined" because this function
    // is actually executed in the scope of the child component!
    window.alert(this.state);
  }
}

This actually makes it pretty awkward to use @Input() properties to pass functions. At the very least it breaks the principle of least surprise and can introduce sneaky bugs.

Of course there are scenarios where maybe you don't need a context. For example, maybe you have a searchable list component that allows complex data as items and need to pass a fnEquals function so the search can determine whether the search input text matches an item. However, these cases are typically better handled by more composable mechanisms (content projection etc.) which increases reusability.

like image 20
Ingo Bürk Avatar answered Oct 05 '22 12:10

Ingo Bürk


There is basically no differences in functionality, but

(i)When you use @input you get advantage of using @Input is that we can define the type and whether it is private or public

(ii)As @ConnorsFan mentioned in the comment, advantage of using @Ouput is many subscribers can handle the Output event, while only one handler can be supplied for the @Input property.

like image 20
Sajeetharan Avatar answered Oct 05 '22 11:10

Sajeetharan