Reputation: 5732
I'm trying to build a list of cards which may contain different components; So for example I have the following array of objects:
{
title: 'Title',
descrption: 'Description',
template: 'table',
},
{
title: 'Title',
descrption: 'Description',
template: 'chart',
}
I get this array as a response from a service, then I need to match each of thos objects to a component based on the template
property, so for example, the first item should match to the TableComponent
and the second one to the ChartComponent
;
I'm trying to follow the Angular Docs regarding Dynamic Component Loading, but I'm not sure how tell the method how to match each object in the array to a specific component.
In my parent component I have made an anchor point where the components should load with a directive:
<ng-template appCheckpointHost></ng-template>
And I'm trying to use the ComponentFactoryResolver
as it shows in the example.
loadComponent() {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(ChartCheckpointComponent);
const viewContainerRef = this.checkHost.viewContainerRef;
}
The example shows a scenario in which the "service" runs every three seconds, gets a random item, and shows it; but what I'm trying to do instead is to fetch all the items when the parent component loads, and render each item with its respective component.
Any ideas to get this to work?
Upvotes: 2
Views: 2660
Reputation: 214047
You can create a dictionary like:
const nameToComponentMap = {
table: TableComponent,
chart: ChartComponent
};
And then just use this dictionary to determine which component should be rendered depending on the template
property of particular item
in your items array:
const componentTypeToRender = nameToComponentMap[item.template];
this.componentFactoryResolver.resolveComponentFactory(componentTypeToRender);
Upvotes: 2
Reputation: 20092
You can view my blog here
First I will need to create a directive to reference to our template instance in view
import { Directive, ViewContainerRef } from "@angular/core";
@Directive({
selector: "[dynamic-ref]"
})
export class DynamicDirective {
constructor(public viewContainerRef: ViewContainerRef) {}
}
Then we simply put the directive inside the view like this
<ng-template dynamic-ref></ng-template>
We put the directive dynamic-ref to ng-content so that we can let Angular know where the component will be render
Next I will create a service to generate the component and destroy it
import {
ComponentFactoryResolver,
Injectable,
ComponentRef
} from "@angular/core";
@Injectable()
export class ComponentFactoryService {
private componentRef: ComponentRef<any>;
constructor(private componentFactoryResolver: ComponentFactoryResolver) {}
createComponent(
componentInstance: any,
viewContainer: any
): ComponentRef<any> {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(
componentInstance
);
const viewContainerRef = viewContainer.viewContainerRef;
viewContainerRef.clear();
this.componentRef = viewContainerRef.createComponent(componentFactory);
return this.componentRef;
}
destroyComponent() {
if (this.componentRef) {
this.componentRef.destroy();
}
}
}
Finally in our component we can call the service like this
@ViewChild(DynamicDirective) dynamic: DynamicDirective;
constructor(
private componentFactoryService: ComponentFactoryService
) {
}
ngOnInit(){
const dynamiCreateComponent = this.componentFactoryService.createComponent(TestComponent, this.dynamic);
(<TestComponent>dynamiCreateComponent.instance).data = 1;
(<TestComponent>dynamiCreateComponent.instance).eventOutput.subscribe(x => console.log(x));
}
ngOnDestroy(){
this.componentFactoryService.destroyComponent();
}
/////////////////////////////////
export class TestComponent {
@Input() data;
@Output() eventOutput: EventEmitter<any> = new EventEmitter<any>();
onBtnClick() {
this.eventOutput.emit("Button is click");
}
}
Upvotes: 1