Создайте компонент Dynami c (ComponentFactoryResolver) с шаблоном Dynami c - PullRequest
0 голосов
/ 29 мая 2020

Я хочу динамически создать компонент с шаблоном Dynami c, чтобы интерполяции этого шаблона разрешались в контексте компонента c Dynami.

Я знаю, что могу использовать это код для создания компонента Dynami c (который должен быть упомянут в entryComponents в модуле):

My stati c component:

@Component({
  selector: 'html-type',
  template: `<ng-template #htmlcontrolcomponent></ng-template>`
})
export class HtmlTypeComponent implements AfterViewInit{

  @ViewChild('htmlcontrolcomponent', { read: ViewContainerRef }) entry: ViewContainerRef;
  constructor(private resolver: ComponentFactoryResolver) {
    super();
   }

  ngAfterViewInit() {
    this.createComponent("<div>{{contextvar}}</div>");
  }

  createComponent(template) {
    this.entry.clear();
    const factory = this.resolver.resolveComponentFactory(HtmlControlComponent);
    const componentRef = this.entry.createComponent(factory);
    componentRef.instance.template = template;       // this doesn't work, is there a way to do it?
  }

Компонент, который следует динамически добавлять:

import { Component} from '@angular/core';

@Component({
  selector: 'html-control',
  template: '',
})
export class HtmlControlComponent {
   contextvar: string = "This is my current context";
}

Есть ли способ переназначить шаблон динамически создаваемого компонента?

Чего я хотел добиться: шаблон компонента Dynami c должен быть динамическим c (введено пользователем и обработано)

Ответы [ 2 ]

0 голосов
/ 29 мая 2020

Я сделал это ... с другим подходом

Я использовал DynamicComponentService

Важно : мне пришлось отключить «aot: false» в angular. json, иначе я получил Runtime compiler is not loaded ошибок.

import {
  Compiler,
  Component,
  ComponentFactory,
  Injectable,
  NgModule,
  Type,
  ViewContainerRef,
  ViewEncapsulation
} from "@angular/core";
import {CommonModule} from "@angular/common";

@Injectable({
  providedIn: "root"
})
export class DynamicComponentService {

  protected cacheOfFactories: {[key: string]: ComponentFactory<any>};
  protected componentCache: {[key: string]: Type<any>};
  protected moduleCache: {[key: string]: Type<any>};

  constructor(protected compiler: Compiler) {
    this.cacheOfFactories = {};
    this.componentCache = {};
    this.moduleCache = {};
  }

  /**
   *
   * @param viewContainerRef
   * @param selector
   * @param template
   */
  createComponentFactory(viewContainerRef: ViewContainerRef, selector: string, template: string) {
    const componentFound = this.componentCache[selector];
    if(componentFound) {
      this.compiler.clearCacheFor(componentFound);
      delete this.componentCache[selector];
    }
    const moduleFound = this.moduleCache[selector];
    if(moduleFound) {
      this.compiler.clearCacheFor(moduleFound);
      delete this.moduleCache[selector];
    }

    viewContainerRef.clear();

    this.componentCache[selector] = Component({
      selector,
      template,
      encapsulation: ViewEncapsulation.None
    })(class {
    });

    this.moduleCache[selector] = NgModule({
      imports: [CommonModule],
      declarations: [this.componentCache[selector]]
    })(class {
    });

    return this.compiler.compileModuleAndAllComponentsAsync(this.moduleCache[selector])
      .then((factories) => {
        const foundFactory = factories.componentFactories.find((factory) => {
          return factory.selector === selector;
        });

        if(foundFactory) {
          return viewContainerRef.createComponent(foundFactory);
        }

        throw new Error("component not found");
      })
      .catch((error) => {
        console.log("error", error);

        this.compiler.clearCacheFor(componentFound);
        delete this.componentCache[selector];
        this.compiler.clearCacheFor(moduleFound);
        delete this.moduleCache[selector];

        return Promise.reject(error);
      });
  }

}

и изменил свой html -тип компонент на:

export class HtmlTypeComponent implements DoCheck {

  @ViewChild('htmlcontrolcomponent', { read: ViewContainerRef }) entry: ViewContainerRef;

  protected oldTemplate: string = "";
  protected componentRef?: ComponentRef<any>;

  constructor(private dynamicComponentService: DynamicComponentService) {}

   ngDoCheck() {
    if(this.entry && this.oldTemplate !== this.to.template) {
      this.oldTemplate = this.to.template;

      if(this.componentRef) {
        this.componentRef.destroy();
        delete this.componentRef;
      }

      this.entry.clear();

      this.dynamicComponentService.createComponentFactory(this.entry, 'html-content', this.to.template)
        .then((component) => {
          this.componentRef = component;
          this.componentRef.instance.model = this.model;
        });
    }
  }

}

Я мог бы даже избавиться от Компонент HtmlControl

0 голосов
/ 29 мая 2020

Вы можете попробовать использовать <ng-content> с параметром projectableNodes функции createComponent(). Попробуйте следующий

import { Component} from '@angular/core';

@Component({
  selector: 'html-control',
  template: `<ng-content></ng-content>`,      // <-- add `<ng-content>` here
})
export class HtmlControlComponent {
   contextvar: string = "This is my current context";
}

stati c component

createComponent(template) {
  this.entry.clear();
  const factory = this.resolver.resolveComponentFactory(HtmlControlComponent);
  const componentRef = this.entry.createComponent(factory, 0, undefined, [[template]]);    // <-- add `template` here
}

Здесь [[template]] аргумент отправляется в projectableNodes параметр типа any[][]


Непроверенный код. Возможно, потребуется сгладить некоторые изгибы.

...