2016-09-24 77 views
8

我试图在最终版本2.0.0中动态加载组件。动态加载现有组件Angular 2最终版本

使用RC5我用下面的代码加载:

创建一个指示加载控件:

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

然后加载该指令在我的网页是这样的:

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

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

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

借此例如:How can I use/create dynamic template to compile dynamic Component with Angular 2.0?

它看起来更特定于该情况下,我的一个我只需要加载组件,并设置一个名为@input模型。

当我尝试时,我必须为每个组件动态创建一个模块,然后将组件添加到它。但后来我有问题说组件被设置在多个模块中,试图在某个地方删除一个不工作。

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

,做了一些改动。

更新

我设法使它工作使用以下方法:

的创建方法已更改为

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将会使用comp躺下。在我的情况下,解决方案是导入我的ControlsModule,其中所有我的控件被导出。

+0

我试图建立一个类似的事情。我有一个加载类和他们的讲座列表的JSON文件。这些课程被添加到导航栏中,讲座将作为详细视图中的列表添加。在点击讲座时,应该在详细视图中加载组件。对于每一个讲座,有不同的成分,我想创建一个模块或路由器链接,每个讲座(和路由器出口)的,而不是只加载喜欢你的例子组件。我仍然试图找出加载事物的最佳方式。的惊人解释 – Atieh

回答

10

更新

NgComponentOutlet4.0.0-beta.3引入https://github.com/angular/angular/commit/8578682

即将NgComponentOutlet

我看到两个选项来做到这一点:

1)使用ComponentFactoryResolver

它使用已经生成的工厂和代码看起来是这样的:

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性能模块

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

2)使用编译

在这种情况下我们仅可以通过使用compiler.compileModuleAndAllComponentsAsync运行模块编译,然后找到从componentFactories阵列部件。您的指令可能是这样的:

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是一个全球性的服务,将加载和存储组件工厂。

@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

希望它可以帮助你!

+1

感谢,你从rango.io提供的链接是exacly什么,我需要做的,因为我的组件是将呈现里面的一些控制一个模式。所有的感谢都是因为这个笨蛋的例子。你帮了我很多。使用ComponentFactoryResolver比编译器有什么优势?我发现使用第一个更容易。 – Abner

+1

不客气! – yurzui

+1

嘿呦嘿嘿有什么优势吗?我看到第二个有缓存,我有点不需要它。我试图找出一种方法来销毁组件,不仅在我重新创建它们时,而且一旦关闭了动态模式。 – Abner