2

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.

1
  • Did the answer worked ? If so, please mark it as an answer so that it can be helpful to others as well Commented Feb 27, 2022 at 13:46

3 Answers 3

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.

Sign up to request clarification or add additional context in comments.

Comments

1

Without using the factory, you have to use ViewContainerRef.createComponent. refer the below link https://stackoverflow.com/a/72174262/19077843

Comments

0

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.