In my simplified scenario there is a parent
component, which contains all the business logic.
The parent
creates a child
component which is only responsible to display a list of buttons.
I want to handle the click events for such buttons in the parent
, but, using the suggested EventEmitter
pattern I'm forced to use a useless switch case
in the parent
, in order to detect which button is pressed.
It would be much easier to directly pass the callbacks as input of the child
, but I think it's not a good practice. Also these callbacks, as far as I can see, are executed in the context of the child, which is not the desired outcome.
Am I missing something? Is there a better way to achieve my needs? Or I'm completely approaching the problem in a wrong way?
The parent:
import { Component } from "@angular/core";
@Component({
selector: "parent",
template: ` <child
[buttons]="buttons"
(click)="onButtonClick($event)"
></child>
<span>{{ description }}</span>`
})
export class ParentComponent {
description = "";
buttons = ["foo", "bar", "baz"];
onButtonClick(button: string) {
switch (button) {
case "foo":
this.description = "Calling foo()";
this.foo();
break;
case "bar":
this.description = "Calling bar()";
this.bar();
break;
case "baz":
this.description = "Calling baz()";
this.baz();
break;
}
}
foo() {}
bar() {}
baz() {}
}
The child:
import { Component, EventEmitter, Input, Output } from "@angular/core";
@Component({
selector: "child",
template: ` <span *ngFor="let button of buttons">
<button (click)="onClick(button)">{{ button }}</button>
</span>`
})
export class ChildComponent {
@Input() buttons: string[];
@Output() click = new EventEmitter<string>();
onClick(button: string) {
this.click.emit(button);
}
}
And a working sandbox.
CodePudding user response:
You could think about providing the callbacks within an object array instead of the string array.
Edited Sandbox: https://codesandbox.io/s/cocky-brahmagupta-ueg4nv
The Parent
import { Component } from "@angular/core";
@Component({
selector: "parent",
template: ` <child
[buttons]="buttons"
></child>
<span>{{ description }}</span>`
})
export class ParentComponent {
description = "";
buttons = [
{ name: "foo", callback: () => { this.description = "Calling foo()" } },
// bar
// baz
];
}
The child
@Component({
selector: "child",
template: ` <span *ngFor="let button of buttons">
<button (click)="button.callback()">{{ button.name }}</button>
</span>`
})
export class ChildComponent {
@Input() buttons: MyButton[];
}
The interface
interface MyButton {
name: string,
callback: () => void;
}
CodePudding user response:
You can emit what-ever
//parent
@Component({
selector: "parent",
template: ` <child
[buttons]="buttons" (click)="$event()"
></child>
<span>{{ description }}</span>`
})
export class ParentComponent {
description = "";
buttons = [
{ name: "foo", callback: this.foo},
{ name: "bar", callback: this.bar},
{ name: "baz", callback: this.baz},
];
foo(){}
bar(){}
baz(){}
}
//child
@Component({
selector: "child",
template: ` <span *ngFor="let button of buttons">
<button (click)="onClick(button)">{{ button.name }}</button>
</span>`
})
export class ChildComponent {
@Input() buttons: any[];
@Output() click = new EventEmitter<any>();
onClick(button: any) {
this.click.emit(button.callback);
}
}
Update Sound "bizarro" pass a function and execute using function(), but we need think that a function is only an object.
Imagine you define two functions
function1(){console.log("I'm function 1")}
function2(){console.log("I'm function 1")}
In an event click we call to a function
click()
{
const mainFunction=this.function1
mainFunction() //execute the function1
}
or according a variable
click()
{
const mainFunction=this.condicion?this.function1:this.function2
mainFunction() //execute the function1 or the function2
//if condition is true or false
}
The variables,object,arrays,functions... are physical stored in a memory position. the "name" of the variable, object, function... are only a "pointer" to this memory.
If we want, we could in the answer write
<child [buttons]="buttons" (click)="executeFunction($event)"></child>
executeFunction(mainFunction:any)
{
mainFunction()
}
If we want we can type the function to give more clearity if all our function has no argument and no return anything, instead use this uggly "any" use
executeFunction(mainFunction:()=>void)
{
mainFunction()
}
And
@Output() click = new EventEmitter<()=>void>();