3

The gist of what I'm trying to do, is create a generic child component that takes as an argument a list of actions of some type. Those actions would be dynamically generated on the child component as buttons and emit an event that would be caught by the parent and invoke the appropriate method there.

I'm trying to avoid just passing in the funciton bound to the parent as it doesnt feel right.

Ideally the parent would pass down data like:

[{ name: 'Edit', method: 'onEdit' }, { name: 'Delete', method: 'onDelete' }]

The child component has an emitter like:

@Output() actionEvent: EventEmitter<MyGreatEvent> = new EventEmitter();

and generates buttons like:

<button mat-menu-item *ngFor="let action of actions" (click)="actionEvent.emit({method: action.method, item: row})">
    {{action.name}}
</button>

where row is the data from a mat-table row.

And then catch it in the parent via event binding with something like:

handleEvent(event: MyGreatEvent): void {
    // ???
    // profit
}

I've tried a handful of approaches, but always lose track of the lexical this, and I end up falling back to vanilla JS to try to solve the problem. Basically everything I can find about emitters describes a 1-to-1 relationship between an event emitter and a method. Trying to get generic but still typescript-y. I feel like I'm missing something. Any help is greatly appreciated!

Edit: I created a Stackblitz to hopefully illustrate my problem.

Phil Mayfield
  • 203
  • 1
  • 2
  • 8
  • 2
    Possible duplicate of [How to access the correct \`this\` inside a callback?](https://stackoverflow.com/questions/20279484/how-to-access-the-correct-this-inside-a-callback) – ConnorsFan Feb 16 '19 at 01:26
  • 1
    Try defining the callback as an arrow function: `handleEvent = (event: MyGreatEvent) => { ... }`. – ConnorsFan Feb 16 '19 at 01:27
  • Is it possible to build a stackblitz prototype that demonstrates your objective? – DeborahK Feb 16 '19 at 01:58
  • Could you please add a small demo for this in stackblitz. – AlokeT Feb 16 '19 at 06:02
  • So your goal is to be able to execute some form of logic using the `row` object depending on which button was clicked? If this is correct, there is a better approach to your issue – Jota.Toledo Feb 16 '19 at 09:06
  • In your example, where are the methods `onEdit` and `onDelete` defined? or are they simple strings that signalize the type of event? Furthermore, _I'm trying to avoid just passing in the funciton bound to the parent as it doesnt feel right._ there is nothing wrong with doing that – Jota.Toledo Feb 16 '19 at 09:12
  • I've added a stackblitz example, thanks! I do know that there is nothing wrong with binding a function, like I said it just doesnt feel like the Angular/Typescript thing to do. I'm trying to create a reusable generic component that has a safety net of interfaces and typing. Hope that makes sense! – Phil Mayfield Feb 17 '19 at 05:45

1 Answers1

0

One way of overcoming this could be naming the functions inside your parent component and pass the values like below. It is always good not to pass parent component instance directly to child as @Input

onEdit(item) {
  // edit item
}

onDelete(item) {
  // delete item
}

handleEvent({method, data }: MyGreatEvent): void {
  if (this[method] && typeof this[method] === 'function') {
    this[method](data);
  }
}
Mukundhan
  • 3,284
  • 23
  • 36