How to load component dynamically using component name in angular2?
I looked far and wide for solution that satisfies Angular 9 requirements for dynamically loaded modules and I came up with this
import {
ComponentFactory,
Injectable,
Injector,
ɵcreateInjector as createInjector,
ComponentFactoryResolver,
Type
} from '@angular/core';
export class DynamicLoadedModule {
public exportedComponents: Type<any>[];
constructor(
private resolver: ComponentFactoryResolver
) {
}
public createComponentFactory(componentName: string): ComponentFactory<any> {
const component = (this.exportedComponents || [])
.find((componentRef) => componentRef.name === componentName);
return this.resolver.resolveComponentFactory(component);
}
}
@NgModule({
declarations: [LazyComponent],
imports: [CommonModule]
})
export class LazyModule extends DynamicLoadedModule {
constructor(
resolver: ComponentFactoryResolver
) {
super(resolver);
}
}
@Injectable({ providedIn: 'root' })
export class LazyLoadUtilsService {
constructor(
private injector: Injector
) {
}
public getComponentFactory<T>(component: string, module: any): ComponentFactory<any> {
const injector = createInjector(module, this.injector);
const sourceModule: DynamicLoadedModule = injector.get(module);
if (!sourceModule?.createComponentFactory) {
throw new Error('createFactory not defined in module');
}
return sourceModule.createComponentFactory(component);
}
}
Usage
async getComponentFactory(): Promise<ComponentFactory<any>> {
const modules = await import('./relative/path/lazy.module');
const nameOfModuleClass = 'LazyModule';
const nameOfComponentClass = 'LazyComponent';
return this.lazyLoadUtils.getComponentFactory(
nameOfComponentClass ,
modules[nameOfModuleClass]
);
}
I know this post is old, but a lot of things have changed in Angular and I didn't really like any of the solutions from an ease of use and safety. Here's my solution that I hope you like better. I'm not going to show the code to instantiate the class because those examples are above and the original Stack Overflow question already showed a solution and was really asking how to get the Class instance from the Selector.
export const ComponentLookupRegistry: Map<string, any> = new Map();
export const ComponentLookup = (key: string): any => {
return (cls) => {
ComponentLookupRegistry.set(key, cls);
};
};
Place the above Typescript Decorator and Map in your project. And you can use it like so:
import {ComponentLookup, ComponentLookupRegistry} from './myapp.decorators';
@ComponentLookup('MyCoolComponent')
@Component({
selector: 'app-my-cool',
templateUrl: './myCool.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MyCoolComponent {...}
Next, and this is important, you need to add your component to entryComponents
in your module. This allows the Typescript Decorator to get called during app startup.
Now anywhere in your code where you want to use Dynamic Components (like several of the above examples) when you have a Class Reference, you just get it from your map.
const classRef = ComponentLookupRegistry.get('MyCoolComponent');
// Returns a reference to the Class registered at "MyCoolComponent
I really like this solution because your KEY that you register can be the component selector, or something else that's important to you or registered with your server. In our case, we needed a way for our server to tell us which component (by string), to load into a dashboard.
Perhaps this will work
import { Type } from '@angular/core';
@Input() comp: string;
...
const factories = Array.from(this.resolver['_factories'].keys());
const factoryClass = <Type<any>>factories.find((x: any) => x.name === this.comp);
const factory = this.resolver.resolveComponentFactory(factoryClass);
const compRef = this.vcRef.createComponent(factory);
where this.comp
is a string name of your Component like "MyComponent"
Plunker Example
To do it working with minification see
- ng2 - dynamically creating a component based on a template