'How do you load Components dynamically in Angular 13?
I was tasked to update an Angular library from 7 to 13. This Library used Compiler and ComponentFactory to dynamically load Components. But those classes are now deprecated, and I found no real guide on how to do this without using these classes. Here's the code in question:
dynamic-content.component.ts:
...
private createCompiledTemplateFactory = (template: string, extensionType: string): ComponentFactory<any> | undefined => {
const metadata = {
selector: `dynamic-extended-component-${extensionType}`,
template: template
};
const extType = ClassInjector.get(extensionType);
if (!extType) {
throw new Error('Type not found: ' + extensionType);
}
return this.createComponentFactorySync(metadata, null, extType);
}
private createComponentFactorySync = (metadata: Component, componentClass: any, extensionType: Type<any>)
: ComponentFactory<any> | undefined => {
const cmpClass = componentClass || class RuntimeComponent extends extensionType { };
const decoratedCmp = Component(metadata)(cmpClass);
const externalImports = this.externalImports;
@NgModule({
imports: [
CommonModule,
FormsModule,
DynamicGridModule.forChild(),
DxButtonModule,
DxSwitchModule,
TranslateModule,
RouterModule,
externalImports,
DynamicPageCommonComponentsModule
],
declarations: [decoratedCmp]
})
class RuntimeComponentModule { }
const module: ModuleWithComponentFactories<any> = this.compiler.compileModuleAndAllComponentsSync(RuntimeComponentModule);
return module.componentFactories.find(f => f.componentType === decoratedCmp);
}
...
class-injector.ts
export class ClassInjector {
private static registry: { [key: string]: Type<any> } = {};
static register(key: string, value: Type<any>) {
const registered = ClassInjector.registry[key];
if (registered) {
throw new Error(`Error: ${key} is already registered.`);
}
ClassInjector.registry[key] = value;
}
static get(key: string): Type<any> {
const registered = ClassInjector.registry[key];
if (registered) {
return registered;
} else {
throw new Error(`Error: ${key} was not registered.`);
}
}
}
export function RegisterActionHandler(name: string) {
return (target: Type<any>) => ClassInjector.register(name, target);
}
I would appreciate it if anyone could guide me in the right direction.
Solution 1:[1]
We found out that the component could work without any mention of compiler:
dynamic-content.component.ts:
...
private createComponent = (template: string, extensionType: string) : Type<any> => {
const metadata = {
selector: `dynamic-extended-component-${extensionType}`,
template: template
};
const extType = ClassInjector.get(extensionType);
if (!extType) {
throw new Error('Type not found: ' + extensionType);
}
return Component(metadata)(class RuntimeComponent extends extType { });
}
private createModule = (component: Type<any>) : Type<any> => {
const externalImports = this.externalImports;
const module = NgModule({
imports: [
CommonModule,
FormsModule,
DynamicGridModule.forChild(),
DxButtonModule,
DxSwitchModule,
TranslateModule,
RouterModule,
externalImports,
DynamicPageCommonComponentsModule
],
declarations: [component]
})(
class RuntimeComponentModule { });
return module;
}
...
class-injector.ts stayed the same.
Solution 2:[2]
You can use cdkPortalOutlet
from here ,
Here is a demo code to play around with as well.
You can also listen to events of rendering as below
<ng-template [cdkPortalOutlet]="portal" (attached)="onComponentRendering($event)"></ng-template>
in the component
this.portal = new ComponentPortal(SomeComponent);
and listen to onComponentRendering
to do more
Solution 3:[3]
Without using the factory, you have to use ViewContainerRef.createComponent
.
refer the below link
https://stackoverflow.com/a/72174262/19077843
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | tsavinho |
Solution 2 | Shashank Vivek |
Solution 3 |