本文介绍了Angular 2.1.0动态创建子组件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 29岁程序员,3月因学历无情被辞! 我在 angular 2.1.0 中尝试做的是动态创建应该注入父组件的子组件。例如,父组件是 lessonDetails ,其中包含所有课程的共享内容,例如等按钮转到上一课,转到下一课和其他内容。根据路线参数,需要子组件的课程内容需要动态注入父组件。子组件的HTML(课程内容)在外面的某处定义为普通字符串,它可以是以下对象:What i'm trying to do in angular 2.1.0 is creating child components on the fly which should be injected into parent component. For example parent component is lessonDetails which contains shared stuff for all lessons such as buttons like Go to previous lesson, Go to next lesson and other stuff. Based on route params, lesson content which should be child component needs to be injected dynamically into parent component. HTML for child components (lesson content) is defined as plain string somewhere outside, it can be object like:export const LESSONS = { "lesson-1": `<p> lesson 1 </p>`, "lesson-2": `<p> lesson 2 </p>`}问题可以通过 innerHtml 在父组件模板中有类似内容。Problem can be easily solved through innerHtml having something like following in parent component template.<div [innerHTML]="lessonContent"></div>每次更改路线参数时,物业 lessonContent 将更改(内容(新模板)将取自 LESSON 对象),从而导致更新父组件模板。这有效,但angular不会处理通过 innerHtml 注入的内容,因此无法使用 routerLink 和其他东西。Where on each change of route params, property lessonContent of parent component would change(content(new template) would be taken from LESSON object) causing parent component template to be updated. This works but angular will not process content injected through innerHtml so it is impossible to use routerLink and other stuff.在新的角度释放之前,我使用来自 http://blog.lacolaco.net/post/dynamic-component-creation-in-angular-2/ ,我一直在使用 ComponentMetadata 与 ComponentResolver 一起动态创建子组件,如:Before new angular release i solved this problem using solution from http://blog.lacolaco.net/post/dynamic-component-creation-in-angular-2/, where i have been using ComponentMetadata together with ComponentResolver to create child components on the fly, like:const metadata = new ComponentMetadata({ template: this.templateString,});其中 templateString 已传递给子组件输入属性到子组件。 MetaData 和 ComponentResolver 在 angular 2.1.0 。Where templateString was passed to child component as Input property to child component. Both MetaData and ComponentResolver are deprecated/removed in angular 2.1.0.所以问题不仅仅是关于动态组件的创建,就像在几个相关的SO问题中描述的那样,如果我为每个问题定义组件,问题会更容易解决课的内容。这意味着我需要为100个不同的课程预先声明100个不同的组件。不推荐使用的元数据提供行为,就像在单个组件的运行时更新模板一样(在路径参数更改时创建和销毁单个组件)。So problem is not just about dynamic component creation, like described in few related SO questions, problem would be easier to solve if i would have defined component for each lesson-content. This would mean that i need to predeclare 100 different components for 100 different lessons. Deprecated Metadata was providing behaviour that was like updating template at runtime of single component(creating and destroying single component on route params change). Update 1:在最近的角度版本中,需要动态创建/注入的所有组件都需要在 entryComponents 中预定义 @NgModule 。因此,在我看来,与上述问题相关,如果我需要100课(需要动态创建的组件),这意味着我需要预定义100个组件Update 1: As it seems in recent angular release, all components that needs to be created/injected dynamically needs to be predefined in entryComponents within @NgModule. So as it seems to me, related to question above, if i need to have 100 lessons(components that needs to be created dynamically on the fly) that means i need to predefine 100 components 更新2:基于更新1,可以通过以下方式通过 ViewContainerRef.createComponent()完成:Update 2: Based on Update 1, it can be done through ViewContainerRef.createComponent() in following way:// 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 @NgModuleexport const LESSON_CONTENT_COMPONENTS = [ LESSON_1, LESSON_2 ]现在在路线参数的父组件中更改Now in parent component on route params changeconst 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() as:Where lessonContentContainer is decorated @ViewChildren property and lessonContent is decorated as @ViewChild and it is initialized in ngAfterViewInit () as:ngAfterViewInit () { this.lessonContentContainer.changes.subscribe((items) => { this.lessonContent = items.first; this.subscription = this.activatedRoute.params.subscribe((params) => { // logic that needs to show lessons }) })} 解决方案有一个缺点,即所有组件(LESSON_CONTENT_COMPONENTS)都需要预定义。 有没有办法使用一个组件并且在运行时更改该组件的模板(在路径参数更改)?Solution has one drawback and that is, all components(LESSON_CONTENT_COMPONENTS) needs to be predefined. Is there a way to use one single component and to change template of that component at runtime (on route params change)?推荐答案您可以使用以下 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示例See also Plunker Example 自定义组件示例对于AOT编译,请参阅这些主题For AOT compilation see these threads https://github.com/angular/angular/issues/15510 http://blog.assaf.co/angular-2-harmony-aot-compilation- with-lazy-jit-2 /https://github.com/angular/angular/issues/15510http://blog.assaf.co/angular-2-harmony-aot-compilation-with-lazy-jit-2/另请参阅 github Webpack AOT示例 https://github.com/alexzuza/angular2-build-ex amples / tree / master / ngc-webpack 这篇关于Angular 2.1.0动态创建子组件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云!
09-02 04:19