簡體   English   中英

如何使用angular2中的組件名稱動態加載組件?

[英]How to load component dynamically using component name in angular2?

我目前正在使用以下代碼在我的應用程序中動態加載角度組件。

export class WizardTabContentContainer {
  @ViewChild('target', { read: ViewContainerRef }) target: any;
  @Input() TabContent: any | string;
  cmpRef: ComponentRef<any>;
  private isViewInitialized: boolean = false;

  constructor(private componentFactoryResolver: ComponentFactoryResolver,   private compiler: Compiler) {
  }

  updateComponent() {
     if (!this.isViewInitialized) {
       return;
     }
     if (this.cmpRef) {
       this.cmpRef.destroy();
     }
     let factory = this.componentFactoryResolver.resolveComponentFactory(this.TabContent);

     this.cmpRef = this.target.createComponent(factory);
   }
}

這里 resolveComponentFactory 函數接受組件類型。 我的問題是,有什么方法可以使用組件名稱字符串加載組件,例如我將組件定義為

export class MyComponent{
}

如何使用組件名稱字符串“MyComponent”而不是類型添加上述組件?

也許這會奏效

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);

其中this.comp是您的組件的字符串名稱,例如"MyComponent"

Plunker 示例

要使用縮小來做到這一點,請參閱

我知道這篇文章已經過時了,但是 Angular 中的很多東西都發生了變化,而且我真的不喜歡任何解決方案的易用性和安全性。 這是我的解決方案,希望您喜歡。 我不打算展示實例化類的代碼,因為上面有這些例子,原始的 Stack Overflow 問題已經展示了一個解決方案,並且真正詢問如何從 Selector 獲取 Class 實例。

export const ComponentLookupRegistry: Map<string, any> = new Map();

export const ComponentLookup = (key: string): any => {
    return (cls) => {
        ComponentLookupRegistry.set(key, cls);
    };
};

將上述 Typescript Decorator 和 Map 放在您的項目中。 你可以像這樣使用它:

import {ComponentLookup, ComponentLookupRegistry} from './myapp.decorators';

@ComponentLookup('MyCoolComponent')
@Component({
               selector:        'app-my-cool',
               templateUrl:     './myCool.component.html',
               changeDetection: ChangeDetectionStrategy.OnPush
           })
export class MyCoolComponent {...}

接下來,這很重要,您需要將組件添加到模塊中的entryComponents中。 這允許在應用程序啟動期間調用 Typescript 裝飾器。

現在,當您擁有類引用時,您希望在代碼中使用動態組件的任何位置(如上面的幾個示例),您只需從地圖中獲取它。

const classRef = ComponentLookupRegistry.get('MyCoolComponent');  
// Returns a reference to the Class registered at "MyCoolComponent

我真的很喜歡這個解決方案,因為您注冊的 KEY 可以是組件選擇器,或者其他對您很重要或在您的服務器上注冊的東西。 在我們的例子中,我們需要一種方法讓我們的服務器告訴我們哪個組件(通過字符串)加載到儀表板中。

我廣泛尋找滿足動態加載模塊的 Angular 9 要求的解決方案,我想出了這個

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

用法

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]
    );
}

也可以通過import訪問:

someComponentLocation.ts - 包含可能組件的枚舉:

export * from './someComponent1.component'
export * from './someComponent2.component'
export * from './someComponent3.component';

導入器組件:

import * as possibleComponents from './someComponentLocation'
...

@ViewChild('dynamicInsert', { read: ViewContainerRef }) dynamicInsert: ViewContainerRef;

constructor(private resolver: ComponentFactoryResolver){}

然后您可以創建組件實例,例如:

let inputComponent = possibleComponents[componentStringName];
if (inputComponent) {
    let inputs = {model: model};
    let inputProviders = Object.keys(inputs).map((inputName) => { return { provide: inputName, useValue: inputs[inputName] }; });
    let resolvedInputs = ReflectiveInjector.resolve(inputProviders);
    let injector: ReflectiveInjector = ReflectiveInjector.fromResolvedProviders(resolvedInputs, this.dynamicInsert.parentInjector);
    let factory = this.resolver.resolveComponentFactory(inputComponent as any);
    let component = factory.create(injector);
    this.dynamicInsert.insert(component.hostView);
}

請注意,組件必須在 @NgModule entryComponents 中

https://stackblitz.com/edit/angular-hzx94e

以下是如何通過字符串加載角度組件。 它也適用於prod構建。

此外,它允許您將數據注入每個動態加載的組件。

我使用 anthor 方法來做到這一點,可能對你有幫助。

1.首先定義一個用作名稱映射組件的類和用於模塊名稱映射 nmc 的類 RegisterNMC

export class NameMapComponent {
  private components = new Map<string, Component>();

  constructor(components: Component[]) {
    for (let i = 0; i < components.length; i++) {
      const component = components[i];
      this.components.set(component.name, component);
    }
  }

  getComponent(name: string): Component | undefined {
    return this.components.get(name);
  }

  setComponent(component: Component):void {
    const name = component.name;
    this.components.set(name, component);
  }

  getAllComponent(): { [key: string]: Component }[] {
    const components: { [key: string]: Component }[] = [];
    for (const [key, value] of this.components) {
      components.push({[key]: value});
    }
    return components;
  }
}

export class RegisterNMC {
  private static nmc = new Map<string, NameMapComponent>();

  static setNmc(name: string, value: NameMapComponent) {
    this.nmc.set(name, value);
  }

  static getNmc(name: string): NameMapComponent | undefined {
    return this.nmc.get(name);
  }

}

type Component = new (...args: any[]) => any;
  1. 在 ngMgdule 文件中,必須將動態加載的組件放在 entryCompoent 中。

    const registerComponents = [WillBeCreateComponent]; const nmc = new NameMapComponent(registerComponents); RegisterNMC.setNmc('component-demo', nmc);

3.在容器組件中

@ViewChild('insert', {read: ViewContainerRef, static: true}) insert: ViewContainerRef;

  nmc: NameMapComponent;
  remoteData = [
    {name: 'WillBeCreateComponent', options: '', pos: ''},
  ];

  constructor(
    private resolve: ComponentFactoryResolver,
  ) {
    this.nmc = RegisterNMC.getNmc('component-demo');

  }

  ngOnInit() {
    of(this.remoteData).subscribe(data => {
      data.forEach(d => {
        const component = this.nmc.getComponent(d.name);
        const componentFactory = this.resolve.resolveComponentFactory(component);
        this.insert.createComponent(componentFactory);
      });
    });
  }

沒關系,希望能幫到你^_^!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM