Компонент Angular Dynamic Html Template с генератором событий - PullRequest
1 голос
/ 23 мая 2019

Я пытаюсь расширить функциональность этого компонента, добавив универсальный источник событий

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)

Это говорит о том, что функция не добавляется в класс должным образом, когдаон создается внутри компонента Factory

Любой Angular Gurus, способный пролить свет на то, как я могу заставить это работать?

1 Ответ

0 голосов
/ 28 мая 2019

Управляется последовательным подключением субъекта в динамическом компоненте к источнику событий в 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();
    }
  }
}
...