Angular 2.1.0dynamic创build子组件
我正在试图做的angular 2.1.0
正在创build应该注入到父组件的飞行中的子组件。 例如,父组件是lessonDetails
,它包含所有课程的共享内容,例如button,如Go to previous lesson
, Go to next lesson
以及其他内容。 基于路由参数,应该是子组件的课程内容需要dynamic注入父组件 。 子组件(课程内容)的HTML被定义为外部的纯string,它可以是如下的对象:
export const LESSONS = { "lesson-1": `<p> lesson 1 </p>`, "lesson-2": `<p> lesson 2 </p>` }
通过innerHtml
在父组件模板中可以很容易地解决问题。
<div [innerHTML]="lessonContent"></div>
如果每次路由参数变化,父组件的属性lessonContent
将改变(内容(新模板)将从LESSON
对象中取得),导致父组件模板被更新。 这个工程,但angular将不处理通过innerHtml
注入的内容,所以不可能使用routerLink
和其他的东西。
在新的angular度释放之前,我使用来自http://blog.lacolaco.net/post/dynamic-component-creation-in-angular-2/的解决scheme解决了这个问题,其中我一直使用ComponentMetadata
和ComponentResolver
一起创build子组件苍蝇,就像:
const metadata = new ComponentMetadata({ template: this.templateString, });
其中templateString
作为子组件的Input
属性传递给子组件。 MetaData
和ComponentResolver
都在angular 2.1.0
被弃用/删除。
所以问题不仅在于dynamic组件的创build,就像在几个相关的SO问题中所描述的一样,如果我为每个课程内容定义了组件,问题将会更容易解决。 这意味着我需要为100个不同的课程预先声明100个不同的组件。 弃用的元数据提供的行为就像在单个组件的运行时更新模板(创build和销毁路由参数更改中的单个组件)。
更新1:在最近的angular度版本中,似乎需要dynamic创build/注入的所有组件都需要在entryComponents
中的@NgModule
预定义。 所以在我看来,与上面的问题相关,如果我需要有100课(需要dynamic地创build组件),这意味着我需要预先定义100个组件
更新2:基于更新1,可通过以下方式通过ViewContainerRef.createComponent()
完成:
// lessons.ts @Component({ template: html string loaded from somewhere }) class LESSON_1 {} @Component({ template: html string loaded from somewhere }) class LESSON_2 {} // exported value to be used in entryComponents in @NgModule export const LESSON_CONTENT_COMPONENTS = [ LESSON_1, LESSON_2 ]
现在在路由参数的父组件改变
const key = // determine lesson name from route params /** * class is just buzzword for function * find Component by name (LESSON_1 for example) * here name is property of function (class) */ const dynamicComponent = _.find(LESSON_CONTENT_COMPONENTS, { name: key }); const lessonContentFactory = this.resolver.resolveComponentFactory(dynamicComponent); this.componentRef = this.lessonContent.createComponent(lessonContentFactory);
父模板如下所示:
<div *ngIf="something" #lessonContentContainer></div>
其中lessonContentContainer
装饰在@ViewChildren
属性和lessonContent
装饰为@ViewChild
和它在ngAfterViewInit ()
初始化为:
ngAfterViewInit () { this.lessonContentContainer.changes.subscribe((items) => { this.lessonContent = items.first; this.subscription = this.activatedRoute.params.subscribe((params) => { // logic that needs to show lessons }) }) }
解决scheme有一个缺点 ,那就是所有组件(LESSON_CONTENT_COMPONENTS)都需要预定义。
有没有办法使用一个单一的组件,并在运行时更改该组件的模板(路线参数更改)?
你可以使用下面的HtmlOutlet
指令:
import { Component, Directive, NgModule, Input, ViewContainerRef, Compiler, ComponentFactory, ModuleWithComponentFactories, ComponentRef, ReflectiveInjector } from '@angular/core'; import { RouterModule } from '@angular/router'; import { CommonModule } from '@angular/common'; export function createComponentFactory(compiler: Compiler, metadata: Component): Promise<ComponentFactory<any>> { const cmpClass = class DynamicComponent {}; const decoratedCmp = Component(metadata)(cmpClass); @NgModule({ imports: [CommonModule, RouterModule], declarations: [decoratedCmp] }) class DynamicHtmlModule { } return compiler.compileModuleAndAllComponentsAsync(DynamicHtmlModule) .then((moduleWithComponentFactory: ModuleWithComponentFactories<any>) => { return moduleWithComponentFactory.componentFactories.find(x => x.componentType === decoratedCmp); }); } @Directive({ selector: 'html-outlet' }) export class HtmlOutlet { @Input() html: string; cmpRef: ComponentRef<any>; constructor(private vcRef: ViewContainerRef, private compiler: Compiler) { } ngOnChanges() { const html = this.html; if (!html) return; if(this.cmpRef) { this.cmpRef.destroy(); } const compMetadata = new Component({ selector: 'dynamic-html', template: this.html, }); createComponentFactory(this.compiler, compMetadata) .then(factory => { const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector); this.cmpRef = this.vcRef.createComponent(factory, 0, injector, []); }); } ngOnDestroy() { if(this.cmpRef) { this.cmpRef.destroy(); } } }
另请参阅Plunker示例
自定义组件的例子
对于AOT编译,请参阅这些线程
- https://github.com/angular/angular/issues/15510
- http://blog.assaf.co/angular-2-harmony-aot-compilation-with-lazy-jit-2/
另请参阅github Webpack AOT示例 https://github.com/alexzuza/angular2-build-examples/tree/master/ngc-webpack