angular - Input vs Output-hendelsesbinding
Jeg ser etter et argument for hvorfor det er bedre å bruke @Output for hendelser enn å sende en @Input-funksjon i Angular 2+.
Bruke @Input:
Foreldre-mal:
<my-component [customEventFunction]=myFunction></my-component>
Inne i parent-component.ts:
myFunction = () => {
console.log("Hello world")
}
Inni my-component.ts
@Input() customEventFunction: Function;
someFunctionThatTriggersTheEvent() {
this.customEventFunction();
}
Bruke @Output:
Foreldre-mal:
<my-component (onCustomEvent)=myFunction()></my-component>
Inne i parent-component.ts:
myFunction() {
console.log("Hello world")
}
Inni my-component.ts
@Output() onCustomEvent: EventEmitter<any> = new EventEmitter<any>();
someFunctionThatTriggersTheEvent() {
this.onCustomEvent.emit();
}
Begge oppnår samme mål, men jeg tror at @Output-metoden er mer typisk ut fra det jeg har sett i andre Angular-pakker. Man kan hevde at med en Input kan du sjekke om funksjonen eksisterer hvis hendelsen kun skal utløses betinget.
Tankene?
Svar #1
Fordelene med @Output-hendelsesbinding:
- Å definere en hendelse med @Output gjør det klart at den forventer tilbakeringingsmetoder for å håndtere hendelsen, ved å bruke den standard Angular-mekanismen og syntaksen.
- Mange hendelseshåndterere kan abonnere på @Output-hendelsen. På den annen side, hvis du definerer en @Input-egenskap som aksepterer en tilbakeringingsfunksjon, kan bare én hendelseshåndterer registreres; tildeling av en andre hendelseshåndterer vil koble fra den første. For å lage en parallell med standard DOM-hendelseshåndterere, ligner @Input-tilbakeringingsfunksjonsbindingen på å sette
onmousemove="doSomething()"mens @Output-hendelsesbindingen er mer som å kallebtn.addEventListener("mousemove", ...).
Svar #2
@Sajeetharan sitt svar er faktisk ikke helt korrekt: det er en betydelig funksjonell forskjell: utførelseskonteksten. Tenk på dette scenarioet:
@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);
}
}
Dette gjør det faktisk ganske vanskelig å bruke @Input()-egenskaper for å sende funksjoner. Minst av alt bryter det prinsippet om minst overraskelse og kan introdusere lumske feil.
Selvfølgelig finnes det scenarier hvor du kanskje ikke trenger en kontekst. For eksempel, kanskje du har en søkbar listekomponent som tillater komplekse data som elementer og trenger å sende en fnEquals-funksjon slik at søket kan avgjøre om søketeksten samsvarer med et element. Imidlertid håndteres disse tilfellene vanligvis bedre av mer komponerbare mekanismer (innholdsprojeksjon osv.), noe som øker gjenbrukbarheten.
