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

查看:94
本文介绍了动态加载现有组件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编译动态组件?

它看起来更具体对于那个场景,我只需要加载组件并设置一个名为model的@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/

并做了几个更改。


更新

Update

我管理使用以下方法使其工作:

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

创建方法已更改为

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

在这种情况下,我们必须在中定义动态组件声明 entryComponents 模块的decorator中的属性

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

    • Dynamically Creating Components With Angular 2.0
    • Plunker Example
    • 2)使用编译器

      在这种情况下,我们只能使用<$ c $运行模块编译c> compiler.compileModuleAndAllComponentsAsync 然后从componentFactorie中查找组件s数组。你的指令可能如下所示:

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

      Plunker Example

      希望对您有所帮助!

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

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