简体   繁体   English

如何使用angular2中的组件名称动态加载组件?

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

I am currently loading angular components dynamically in my application using following code.我目前正在使用以下代码在我的应用程序中动态加载角度组件。

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

Here resolveComponentFactory function accepts component type.这里 resolveComponentFactory 函数接受组件类型。 My question is, Is there any way I can load component using component name string eg I have component defined as我的问题是,有什么方法可以使用组件名称字符串加载组件,例如我将组件定义为

export class MyComponent{
}

How can I add above component using component name string "MyComponent" instead of type?如何使用组件名称字符串“MyComponent”而不是类型添加上述组件?

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"其中this.comp是您的组件的字符串名称,例如"MyComponent"

Plunker Example Plunker 示例

To do it working with minification see要使用缩小来做到这一点,请参阅

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.我知道这篇文章已经过时了,但是 Angular 中的很多东西都发生了变化,而且我真的不喜欢任何解决方案的易用性和安全性。 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.我不打算展示实例化类的代码,因为上面有这些例子,原始的 Stack Overflow 问题已经展示了一个解决方案,并且真正询问如何从 Selector 获取 Class 实例。

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.将上述 Typescript Decorator 和 Map 放在您的项目中。 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.接下来,这很重要,您需要将组件添加到模块中的entryComponents中。 This allows the Typescript Decorator to get called during app startup.这允许在应用程序启动期间调用 Typescript 装饰器。

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.我真的很喜欢这个解决方案,因为您注册的 KEY 可以是组件选择器,或者其他对您很重要或在您的服务器上注册的东西。 In our case, we needed a way for our server to tell us which component (by string), to load into a dashboard.在我们的例子中,我们需要一种方法让我们的服务器告诉我们哪个组件(通过字符串)加载到仪表板中。

I looked far and wide for solution that satisfies Angular 9 requirements for dynamically loaded modules and I came up with this我广泛寻找满足动态加载模块的 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);
    }
}

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

It's also possible to access through import :也可以通过import访问:

someComponentLocation.ts - contains enum of possible components: someComponentLocation.ts - 包含可能组件的枚举:

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

importer component:导入器组件:

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

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

constructor(private resolver: ComponentFactoryResolver){}

then you can create instance of component for example:然后您可以创建组件实例,例如:

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

note that component has to be in @NgModule entryComponents请注意,组件必须在 @NgModule entryComponents 中

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

Here is how you can load angular components by string. 以下是如何通过字符串加载角度组件。 It will also work for prod builds. 它也适用于prod构建。

Additionally, it allows you to inject data into each dynamically loaded component. 此外,它允许您将数据注入每个动态加载的组件。

i user anthor way to done this, may be helpful for you.我使用 anthor 方法来做到这一点,可能对你有帮助。

1.first defined a class which use as name map componet and class RegisterNMC for moduleName map nmc 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. in the ngMgdule file,you must put the components which be dynamically load in entryCompoent.在 ngMgdule 文件中,必须将动态加载的组件放在 entryCompoent 中。

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

3.in the container component 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);
      });
    });
  }

it's fine, hopefully can help you ^_^!没关系,希望能帮到你^_^!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM