动态加载现有组件 Angular 2 Final Release [英] Load existing components dynamically Angular 2 Final Release

查看:26
本文介绍了动态加载现有组件 Angular 2 Final Release的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在最终版本 2.0.0 中动态加载一个组件.

I'm trying to load dynamically a component in the final release 2.0.0.

使用 RC5 我使用以下代码加载:

Using RC5 I was loading using the following code:

创建一个指令来加载控件:

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>

但是从 rc5 更新到 2.0.0 最终版本后,解析器不再存在,被编译器取代.

But after updating from rc5 to 2.0.0 final release, the resolver doesn't exist anymore, was replaced by compiler.

我发现很多地方都展示了如何使用不同的代码加载它,但所有这些都太复杂了,我无法让它工作.

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.

以这个为例:如何使用/创建动态模板来使用 Angular 2.0 编译动态组件?

它看起来更适合那个场景,我的那个我只需要加载组件并设置一个名为模型的@Input.

It looks more specific to that scenario, my one I just need to load the component and set an @Input called model.

当我尝试时,有一件事我必须为每个组件动态创建一个模块,然后将组件添加到其中.但是后来我遇到了一个问题,说该组件被设置在多个模块中,请尝试在某个地方删除但不起作用.

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.

显示的代码的主要部分,我从这个链接得到:http://blog.lacolaco.net/post/dynamic-component-creation-in-angular-2-rc-5/

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/

并做了一些更改.

更新

我设法使其工作,使用以下方法:

I manage to make it work, using the following approach:

create 方法已更改为

The create method has been changed to

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

我发现的大多数地方,设置创建组件并将其设置为 DynamicModule,问题是当您已经在不同的模块中声明相同的组件时,angular 会抱怨.在我的情况下,解决方案是导入我的 ControlsModule,其中导出了我的所有控件.

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. The solution in my case was to import the my ControlsModule that has all my controls being exported.

推荐答案

更新

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

即将推出 NgComponentOutlet

我看到了两个选项:

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

在这种情况下,我们必须在模块的装饰器中的declarationsentryComponents 属性中定义动态组件

In this case we have to define dynamic component in declarations and entryComponents properties within decorator of module

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

  • 使用 Angular 2.0 动态创建组件
  • Plunker 示例
  • 2) 使用编译器

    在这种情况下,我们只能使用 compiler.compileModuleAndAllComponentsAsync 运行模块编译,然后从 componentFactories 数组中找到组件.您的指令可能如下所示:

    In this case we can only run module compilation by using compiler.compileModuleAndAllComponentsAsync and then find component from componentFactories array. 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 是一个全局服务,用于加载和存储组件工厂.

    DynamicLoaderService is a global service that will load and store component factories.

    @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 示例

    希望对你有帮助!

    这篇关于动态加载现有组件 Angular 2 Final Release的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆