带有事件发射器的Angular Dynamic Html模板组件

时间:2019-05-23 16:10:37

标签: angular angular-factory angular-compiler

我正在尝试通过添加通用事件发射器来扩展此组件的功能

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

鸣谢原始作者 https://gist.github.com/benjamincharity/8116414c7f38cffe3cef0e44fe44295d

所需事件的示例

@Output() genericEventEmitter = new EventEmitter<string>();

emitEvent(data:string){
this.genericEventEmitter.emit(data)
}

我尝试将其添加到htmlOutlet类以及DynamicComponent中,但我收到了错误消息

ERROR TypeError: _co.emitEvent is not a function
    at Object.eval [as handleEvent] (DynamicComponent.html:6)

这是告诉我在componentFactory中创建该函数时未正确将其添加到类中

有没有对Angular Gurus有所了解的人,如何使它起作用?

1 个答案:

答案 0 :(得分:0)

设法将动态组件中的主题以菊花链方式链接到html出口中的事件发射器,该事件发射器可以由html出口的父级捕获

import {
  Component,
  Directive,
  NgModule,
  Input,
  ViewContainerRef,
  Compiler,
  ComponentFactory,
  ModuleWithComponentFactories,
  ComponentRef,
  ReflectiveInjector,
  EventEmitter,
  Output,


} from '@angular/core';
import { Observable, Subject } from 'rxjs'

import { RouterModule } from '@angular/router';
import { CommonModule } from '@angular/common';
import { PartialObject } from 'lodash';

export function createComponentFactory(compiler: Compiler, metadata: Component): Promise<ComponentFactory<any>> {
  const cmpClass = class DynamicComponent {
    outputter: Subject<Object> = new Subject();
    genEmit(data) {
      this.outputter.next(data)
    }

  };
  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>;
  @Output() genericEmitter = new EventEmitter();


  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, []);
        this.cmpRef.instance.outputter.subscribe(v => {
          this.genericEmitter.emit(v)

        })



      });

  }

  ngOnDestroy() {
    if (this.cmpRef) {
      this.cmpRef.destroy();
    }
  }
}