I need to call a common method from an interface which is implemented in dynamic child components from parent component in Angular. My parent html component will look like this :
parent.component.html :
<div *ngFor = "let config of childConfigs">
<div *ngIf = " config.type == 'a' ">
<child-a [config]="config"></child-a>
</div>
<div *ngIf = " config.type == 'b' ">
<child-b [config]="config"></child-b>
</div>
.
.
<div *ngIf = " config.type == 'n' ">
<child-n [config]="config"></child-n>
</div>
</div>
<button (click)="resetComponent()"> Reset</button>
Suppose there is an interface 'ComponentActions' which contains a method resetComponent() and all child components implements it. Sample child components structure will be like this
child-a.component.ts :
export class ChildAComponent implements ComponentActions {
@Input() config;
resetComponent(){
// do something
}
}
How can I involk this method in child components by a button click from parent?
Right, that's a tricky one. Your child components all inherits a base interface. There is a way to implement this. You are however going to need to adjust all your component types and change your interface to an abstract class. Don't worry, if it's an abstract class with no defined logic, it will act the same as an interface and you can use implements
, but this way you do not need to create an InjectionToken
:
export abstract class ComponentActions {
resetComponent(): void;
}
If you cannot, or do not want to make it an interface, do the following:
export const ComponentActionsToken = new InjectionToken<ComponentActions>('component actions');
With this in place, you can provide the following to all your child components, so for every child component you put as useExisting
the corresponding child class:
@Component({
selector: 'child-x',
providers: [{ provide: ComponentActions, useExisting: ChildXComponent }]
})
export class ChildXComponent implements ComponentActions {
resetComponent(): void {
// do something
}
}
@Component({
selector: 'child-y',
providers: [{ provide: ComponentActions, useExisting: ChildYComponent }]
})
export class ChildYComponent implements ComponentActions {
resetComponent(): void {
// do something
}
}
If you are using the injection token, you have to change the ComponentActions
value in the provide property to ComponentActionsToken
Now it feels like, according to your template, that you can have multiple instances of a ComponentActions
in your parent template. So you need some logic to determine which is the one you want to perform an action on. But I suppose you have that in place.
Besides that, it also looks like, you want to perform this action on all components at the same time. So this is where the ViewChildren
decorator comes in place:
@Component({
selector: 'parent'
})
export class ParentComponent {
@ViewChildren(ComponentActions)
children?: QueryList<ComponentActions>
resetComponent(): void {
this.children?.forEach((child) => child.resetComponent());
}
}
If you are using the injection token, you have to change the ComponentActions
value in the ViewChildren
to ComponentActionsToken
That's all. Be aware though, that this is a bit untested code, but it should work. If not, just let me know