Reputation: 37
The binding works fine for title, subtitle, button.icon and button.name but not for button.action
parent.component.html
<app-title [title]="title" [subtitle]="subtitle" [buttons]="buttons"></app-title>
parent.component.ts
export class ParentComponent {
actionOne() {
...
}
title = 'Title';
subtitle = 'Subtitle';
buttons = [
{ 'name': 'Name1', 'icon': 'Icon1', 'action': 'actionOne()'},
{ 'name': 'Name2', 'icon': 'Icon2', 'action': 'actionTwo()'},
{ 'name': 'Name3', 'icon': 'Icon3', 'action': 'actionThree()'}
];
}
child.component.html
<section>
<div class="text-wrapper">
<h1>{{ title }}</h1>
<h2 *ngIf="subtitle">{{ subtitle }}</h2>
</div>
<template *ngIf="buttons">
<div class="buttons-wrapper">
<button *ngFor="let button of buttons" md-raised-button (click)="button.action"><md-icon *ngIf="button.icon">{{ button.icon }}</md-icon>{{ button.name }}</button>
</div>
</template>
</div>
child.component.ts
export class ChildComponent {
@Input() title:string;
@Input() subtitle:string;
@Input() buttons:string;
}
Upvotes: 1
Views: 2447
Reputation: 5470
This is possible to do:
Button interface:
export interface IButton {
Name: string;
Icon: string;
Action: Function
}
Parent component:
@Component({
...
})
export class ParentComponent implements OnInit {
buttons: IButton[] = [
{
Name: 'Hello',
Icon: 'Test',
Action: this.actionOne.bind(this) // we need to make sure this is scoped correctly
}
];
actionOne(){
console.log('This is from action One');
}
constructor() { }
ngOnInit() {
}
}
Child component
@Component({
...
})
export class ChildComponent implements OnInit {
@Input() buttons: IButton[];
constructor() { }
ngOnInit() {
}
}
Child html
<div *ngIf="buttons">
<button *ngFor="let button of buttons" (click)="button.Action()">{{button.Name}}</button>
</div>
Hope that helps
Upvotes: 1
Reputation: 14087
Your code cannot work, since you are passing string literals, not method references, to the child. For instance, in the button.action
property, your ChildComponent
sees the string literal "actionOne()"
, not the method actionOne()
.
In order to pass the method references when declaring the buttons, you'd have to remove the quotes and parentheses around the method names and prefix them with this.
:
buttons = [
{ 'name': 'Name1', 'icon': 'Icon1', 'action': this.actionOne },
{ 'name': 'Name2', 'icon': 'Icon2', 'action': this.actionTwo },
{ 'name': 'Name3', 'icon': 'Icon3', 'action': this.actionThree }
]
But still, it won't work as the context for this
will be lost in the ChildComponent
.
What I would do is use string literals in the buttons declarations (similar to your code, but WITHOUT the parentheses, e.g. 'actionOne'
, 'actionTwo'
...) and whenever a button is clicked in the child, I would emit an @Output
event to the parent:
@Component({
template: `
<button (click)="buttonClicked.emit(button.action)">
{{ button.name }}
</button>
`
})
export class ChildComponent {
@Output() buttonClicked: EventEmitter<string> = new EventEmitter<string>();
}
Notice that I'm passing the button.action
property (string) when emitting the event.
Now, the ParentComponent
can listen to that event and use the received string as an identifier to decide which local method to call:
@Component({
template: `
<child-comp (buttonClicked)="handleAction($event)"></child-comp>
`
})
export class ParentComponent {
handleAction(actionName) {
// Here, put some logic to call a local method based on `actionName`
// Something like this[actionName]();
}
actionOne() {
...
}
actionTwo() {
...
}
}
Upvotes: 0