Как ввести компонент программно в указанное место ? - PullRequest
0 голосов
/ 09 января 2020

Как создать экземпляр компонента программно и вставить его в указанное место в шаблоне?

Stackblitz: https://stackblitz.com/edit/angular-do71xk

import {Component, ComponentFactoryResolver, TemplateRef, ViewChild, ViewContainerRef, Type} from "@angular/core";

export interface PaneOptions {
  component: Type<any>;
  params?: any;
}

@Component({
  selector: 'app-manager',
  template: `
    <ng-template #paneContainer></ng-template> <!-- probably redundant -->

    <!-- window template -->
    <ng-template #paneTemplate>
      <div style="background: red">
        <h3>Window header</h3>
        <ng-template #paneBody></ng-template> <!-- or another ng-template -->
      </div>
    </ng-template>
  `
})
export class ManagerComponent {
  @ViewChild('paneContainer', {read: ViewContainerRef}) paneContainer: ViewContainerRef;
  @ViewChild('paneTemplate', {read: ViewContainerRef}) paneTemplateRef: ViewContainerRef;
  @ViewChild('paneTemplate', {read: TemplateRef}) paneTemplate: TemplateRef<any>;

  constructor(private componentFactoryResolver: ComponentFactoryResolver) {}

  addPane(options: PaneOptions) {

    // create embedded view of pane template
    const embeddedView = this.paneContainer.createEmbeddedView(this.paneTemplate);

    // resolve component and insert it into pane container
    // instead, we need to clone #paneTemplate and insert component within <ng-content>
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(options.component);
    const component = this.paneTemplateRef.createComponent(componentFactory);

    Object.assign(component.instance, options.params);
  }
}

И это другой подход, когда Я пытаюсь создать экземпляр компонента внутри внутреннего ng-шаблона.

import {Component, ComponentFactoryResolver, TemplateRef, ViewChild, ViewContainerRef, Type} from "@angular/core";

export interface PaneOptions {
  component: Type<any>;
  params?: any;
}

@Component({
  selector: 'app-manager',
  template: `
    <ng-template #paneContainer></ng-template>

    <!-- pane template -->
    <ng-template #paneTemplate>
      <div style="background: red">
        <h3>Below within red area should be the inner component</h3>
        <ng-template #inner></ng-template>
      </div>
    </ng-template>
  `
})
export class ManagerComponent {
  @ViewChild('paneContainer', {read: ViewContainerRef}) paneContainer: ViewContainerRef;
  @ViewChild('inner', {read: ViewContainerRef}) innerTemplateRef: ViewContainerRef;
  @ViewChild('paneTemplate', {read: TemplateRef}) paneTemplate: TemplateRef<any>;

  constructor(private componentFactoryResolver: ComponentFactoryResolver) {}

  addPane(options: PaneOptions) {

    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(options.component);
    const component = this.innerTemplateRef.createComponent(componentFactory);
    Object.assign(component.instance, options.params);

    // create embedded view of pane template
    const embeddedView = this.paneContainer.createEmbeddedView(this.paneTemplate);
  }
}

Stackblitz: https://stackblitz.com/edit/angular-dzx7vz

Однако я получаю ошибку:

Ошибка: невозможно прочитать свойство 'createComponent' с неопределенным

Я пишу оконный менеджер, используя Angular. Можно ли внедрить компонент в указанное место шаблона окна, а затем вставить окно в основной контейнер? Я хочу сохранить ссылку на каждое окно, чтобы иметь возможность удалить его дальше.

1 Ответ

1 голос
/ 09 января 2020

Для этого вам нужно будет сделать три основных изменения.

  1. Необходимо написать директиву host, в которой будет размещен ваш динамический компонент, как описано здесь - https://angular.io/guide/dynamic-component-loader

  2. Уловить директиву вашего хоста, используя

    @ViewChild(HostDirective, {static: false}) hostDirective;

  3. Задержка загрузки вашего компонента до следующего обнаружения изменений - используя setTimeout

Вы можете сослаться на этот стек Blitz https://stackblitz.com/edit/angular-kbjr75

...