简体   繁体   English

动态加载现有组件Angular 2 Final Release

[英]Load existing components dynamically Angular 2 Final Release

I'm trying to load dynamically a component in the final release 2.0.0. 我正在尝试在最终版本2.0.0中动态加载组件。

Using RC5 I was loading using the following code: 使用RC5我使用以下代码加载:

Create a directive to load the controls: 创建一个指令来加载控件:

import {
  CheckboxComponent, CheckboxListComponent,DatePickerComponent
} from '../components/';

@Directive({
      selector: '[ctrl-factory]'
    })
    export class ControlFactoryDirective implements OnChanges {
      @Input() model: any;

      constructor(private vcRef: ViewContainerRef, private resolver: ComponentResolver) {
      }

      create(cp) {
        this.resolver.resolveComponent(cp)
          .then(factory => {
            const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector);
            this.vcRef.createComponent(factory, 0, injector, []);
            let ch = this.vcRef.createComponent(factory, 0, injector, []).instance;
            ch.model = this.model;
        });
      }

      ngOnChanges() {
        if (!this.model) return;

        switch (this.model.type) {
          case 'checkbox':
            this.create(CheckboxComponent);
            break;
          case 'checkboxlist':
            this.create(CheckboxListComponent);
            break;
          case 'datepicker':
            this.create(DatePickerComponent);
            break;
          default:
            break;
        }
      }
    }

Then loaded that directive in my page like this: 然后在我的页面中加载该指令,如下所示:

<div ctrl-factory *ngFor="let child of page.childrens" [model]="child"></div>

But after updating from rc5 to 2.0.0 final release, the resolver doesn't exist anymore, was replaced by compiler. 但是从rc5更新到2.0.0最终版本后,解析器不再存在,被编译器取代。

I found loads of places showing how to load it using different codes, but all those too complex and I couldn't make it work. 我发现大量的地方显示如何使用不同的代码加载它,但所有那些太复杂,我无法使其工作。

Take this for instance: How can I use/create dynamic template to compile dynamic Component with Angular 2.0? 以此为例: 我如何使用/创建动态模板来使用Angular 2.0编译动态组件?

It looks more specific to that scenario, my one I just need to load the component and set an @Input called model. 它看起来更具体到那个场景,我只需要加载组件并设置一个名为model的@Input。

One thing when I was trying I had to create dynamically a module for each component, then add the component to it. 我尝试的一件事是我必须为每个组件动态创建一个模块,然后将组件添加到它。 But then I had issues saying that the component was being set in more than one Module, try to remove in some place an not working. 但后来我遇到的问题是该组件被设置在多个模块中,尝试在某些地方删除不起作用。

The major part of the code shown, I get from this link: http://blog.lacolaco.net/post/dynamic-component-creation-in-angular-2-rc-5/ 显示的代码的主要部分,我从这个链接: http//blog.lacolaco.net/post/dynamic-component-creation-in-angular-2-rc-5/

And did a couple of changes. 并做了一些改变。

Update 更新

I manage to make it work, using the following approach: 我设法使用以下方法使其工作:

The create method has been changed to create方法已更改为

private create(cp) {
    @NgModule({
      imports: [BrowserModule, ControlsModule],
      declarations: []
    })
    class DynamicModule {}

    this.compiler.compileModuleAndAllComponentsAsync(DynamicModule)
      .then(({componentFactories}) => {
        const compFactory = componentFactories.find(x => x.componentType === cp);
        const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector);
        const cmpRef = this.vcRef.createComponent(compFactory, 0, injector, []);
        cmpRef.instance.model = this.model;
      });
  }

Most places I've found, set create the Component and set it to the DynamicModule, the issue with that is when you are already declaring that same component in a different module, angular is going to complain about. 我找到的大多数地方,设置创建组件并将其设置为DynamicModule,问题是当你已经在另一个模块中声明相同的组件时,angular会抱怨。 The solution in my case was to import the my ControlsModule that has all my controls being exported. 在我的情况下,解决方案是导入我的ControlsModule,我的所有控件都被导出。

Update 更新

NgComponentOutlet was introduced in 4.0.0-beta.3 https://github.com/angular/angular/commit/8578682 NgComponentOutlet是在4.0.0-beta.3中引入的.https ://github.com/angular/angular/commit/8578682

Coming soon NgComponentOutlet 即将推出的NgComponentOutlet

I see two options to do that: 我看到两个选项:

1) Using ComponentFactoryResolver . 1)使用ComponentFactoryResolver

It uses the already generated factory and the code looks something like this: 它使用已经生成的工厂,代码如下所示:

constructor(private vcRef: ViewContainerRef, private resolver: ComponentFactoryResolver) { }
create(comp) {
  const factory = this.resolver.resolveComponentFactory(comp);
  const compRef = this.vcRef.createComponent(factory);

  (<any>compRef).instance.model = this.model;
}

In this case we have to define dynamic component in declarations and entryComponents properties within decorator of module 在这种情况下,我们必须在模块的decorator中的declarationsentryComponents属性中定义动态组件

@NgModule({
  imports:      [ BrowserModule ],
  declarations: [ AppComponent, DynamicComponent ],
  entryComponents: [DynamicComponent],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

2) Using Compiler 2)使用编译器

In this case we can only run module compilation by using compiler.compileModuleAndAllComponentsAsync and then find component from componentFactories array. 在这种情况下,我们只能使用compiler.compileModuleAndAllComponentsAsync运行模块compiler.compileModuleAndAllComponentsAsync ,然后从componentFactories数组中查找组件。 Your directive might look like this: 您的指令可能如下所示:

constructor(private vcRef: ViewContainerRef, private loader: DynamicLoaderService) { }

create(comp) {
  this.loader.createComponentFactory(comp).then(factory => {
     const compRef = this.vcRef.createComponent(factory);

    (<any>compRef).instance.model = this.model;
  })
}

DynamicLoaderService is a global service that will load and store component factories. DynamicLoaderService是一个加载和存储组件工厂的全局服务。

@Injectable()
export class DynamicLoaderService {
  constructor(protected compiler: Compiler) {}

  private resolveCompHelper$ = new Subject<any>();
  private cache = new Map<string, ComponentFactory<any> | number>();

  public createComponentFactory(type: string) : Promise<ComponentFactory<any>> {
    let factory = this.cache.get(type);

    // if factory has been already loading
    if(factory === 1) {
      return new Promise((resolve) => {
        // waiting compilation of factory
        const subscriber = this.resolveCompHelper$.subscribe((data) => {
          if(type !== data.type) return;
          subscriber.unsubscribe();
          resolve(data.factory);
        });   
      });
    } 
    // factory exists in cache
    if (factory) {
      return new Promise((resolve) => resolve(factory));
    }

    const comp = typeMap[type];
    // factory startes loading
    this.cache.set(type, 1);
    return new Promise((resolve) => {
      this.compiler.compileModuleAndAllComponentsAsync(createComponentModule(comp))
        .then((moduleWithFactories: ModuleWithComponentFactories<any>) =>  {
            factory = moduleWithFactories.componentFactories
              .find(x => x.componentType === comp);
            this.cache.set(type, factory);
            this.resolveCompHelper$.next({ type, factory});

            resolve(factory);
        });
    });
  }
}

Plunker Example Plunker示例

Hope it helps you! 希望它能帮到你!

It was useful for me to see this option based on a Directive, but I found the one that fit my needs based on Components: https://www.ag-grid.com/ag-grid-angular-aot-dynamic-components/ 根据指令看到这个选项对我很有用,但我根据组件找到了符合我需求的选项: https//www.ag-grid.com/ag-grid-angular-aot-dynamic-components /

Happy coding! 快乐的编码!

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

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