Angular 13 如何动态加载组件?

How do you load Components dynamically in Angular 13?

我的任务是将 Angular 库从 7 更新到 13。该库使用 Compiler 和 ComponentFactory 来动态加载组件。但是那些 classes 现在已被弃用,而且我没有找到关于如何在不使用这些 classes 的情况下执行此操作的真正指南。 这是有问题的代码:

动态-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);
}

如果有人能指导我正确的方向,我将不胜感激。

您可以使用 cdkPortalOutlet from here ,

这里是demo code to play around with as well.

您还可以监听如下渲染事件

<ng-template [cdkPortalOutlet]="portal" (attached)="onComponentRendering($event)"></ng-template>

在组件中

this.portal = new ComponentPortal(SomeComponent);

onComponentRendering做更多

我们发现该组件可以在不提及编译器的情况下工作:

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 保持不变。

不使用工厂,就得用ViewContainerRef.createComponent。 参考下面link