Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/424.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/angular/26.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/http/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript Angular 2.1.0动态创建子组件_Javascript_Angular - Fatal编程技术网

Javascript Angular 2.1.0动态创建子组件

Javascript Angular 2.1.0动态创建子组件,javascript,angular,Javascript,Angular,在angular 2.1.0中,我试图做的是动态创建子组件,应该将其注入父组件。例如,父组件是lessonDetails,它包含所有课程的共享内容,例如按钮转到上一课,转到下一课和其他内容。基于路由参数,应该是子组件的课程内容需要动态注入到父组件中。子组件(课程内容)的HTML定义为外部某处的纯字符串,它可以是如下对象: export const LESSONS = { "lesson-1": `<p> lesson 1 </p>`, "lesson-2": `&

angular 2.1.0
中,我试图做的是动态创建子组件,应该将其注入父组件。例如,父组件是
lessonDetails
,它包含所有课程的共享内容,例如按钮
转到上一课
转到下一课
和其他内容。基于路由参数,应该是子组件的课程内容需要动态注入到父组件中。子组件(课程内容)的HTML定义为外部某处的纯字符串,它可以是如下对象:

export const LESSONS = {
  "lesson-1": `<p> lesson 1 </p>`,
  "lesson-2": `<p> lesson 2 </p>`
}
其中,在每次更改路由参数时,父组件的属性
lessonContent
将发生更改(内容(新模板)将取自
LESSON
对象),从而导致更新父组件模板。这是可行的,但angular不会处理通过
innerHtml
注入的内容,因此不可能使用
routerLink
和其他东西

在新的angular release之前,我使用solution from解决了这个问题,我一直在使用
ComponentMetadata
ComponentResolver
动态创建子组件,如:

const metadata = new ComponentMetadata({
  template: this.templateString,
});
其中,
templateString
作为子组件的
Input
属性传递给子组件。
元数据
组件解析器
都在
angular 2.1.0
中被弃用/删除

所以问题不仅仅是关于动态组件的创建,就像在一些相关的So问题中描述的那个样,若我为每节课的内容定义了组件,那个么问题将更容易解决。这意味着我需要为100节不同的课预先准备100个不同的组件。不推荐使用的元数据提供的行为类似于在单个组件的运行时更新模板(在路由参数更改时创建和销毁单个组件)

更新1:在最近的angular发行版中,需要动态创建/注入的所有组件都需要在
@NgModule
entryComponents
中预定义。所以在我看来,和上面的问题相关,如果我需要100个课程(需要动态创建的组件),这意味着我需要预定义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>

解决方案有一个缺点,即所有组件(课程内容组件)都需要预定义。
是否有办法使用单个组件并在运行时更改该组件的模板(路由参数更改)?

您可以使用以下
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();
    }    
  }
}
导入{
组成部分,
指令,
NgModule,
输入,
ViewContainerRef,
编译程序,
组件工厂,
模块化组件工厂,
组件参考,
反射射束
}从“@angular/core”开始;
从'@angular/router'导入{RouterModule};
从“@angular/common”导入{CommonModule};

导出函数createComponentFactory(编译器:编译器,元数据:组件):PromiseSee。添加HTML只会添加HTML,如果需要动态组件,可以使用
ViewContainerRef.createComponent()
。否则,只为静态添加到组件模板中的选择器创建组件和指令。@GünterZöchbauer感谢您的回复,实际上我使用的是
ViewContainerRef.createComponent()
请选中问题中的更新2部分。您无法在运行时修改组件模板。有多种方法可以在运行时创建新组件。我不知道这方面的细节,但类似的问题也有答案。类似的问题也在这里,谢谢你的回答。还有一个问题,我们是否应该保存对当前
cmpRef
的引用,并在创建新的动态组件之前手动销毁它?在HtmlOutlet指令中有类似的内容,
private cmpRef:ComponentRef
然后在创建新组件之前在
ngOnChanges的内部
如果(this.cmpRef){this.cmpRef.destroy();}
。或者它会被自动销毁?是的,当然。我们必须手工操作。我不确定,但似乎
这个.vcRef.clear
也做同样的事情。我只更新了一个问题,和这个问题有点无关。尝试使用此解决方案将
safeHtml
pipe-on-html outlet指令作为
相关答案应用于此解决方案,但由于新的angular cli版本,它引发了以下错误:未找到“DynamicHtmlModule”的NgModule元数据。有什么建议吗?
ngAfterViewInit () {
  this.lessonContentContainer.changes.subscribe((items) => {
    this.lessonContent = items.first;
    this.subscription = this.activatedRoute.params.subscribe((params) => {
      // logic that needs to show lessons
    })
  })
}
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();
    }    
  }
}