Angular. Вставить компонент в body

Функциональность требуется для диалогов, уведомлений и других элементов наложения. Данная реализация манипулирует только одним компонентом в один момент времени. Позволяет вставить компонент с нужными Input()ами.

Вариант добавления вручную

@Injectable({
  providedIn: 'root',
})
export class DomService {
  /** ref to window document */
  private readonly document: Document;
  /** renderer instance */
  private readonly renderer: Renderer2;
  /** attached component  */
  private componentRef: ComponentRef<unknown>;

  constructor(
    @Inject(DOCUMENT) document,
    private componentFactoryResolver: ComponentFactoryResolver,
    private injector: Injector,
    private applicationRef: ApplicationRef,
    rendererFactory: RendererFactory2
  ) {
    this.renderer = rendererFactory.createRenderer(null, null);
    this.document = document;
  }

  attachComponent<T>(component: Type<T>, componentProps: object = null): T {
    const componentRef = this.componentFactoryResolver
      .resolveComponentFactory(component)
      .create(this.injector);
    if (componentProps !== null && typeof componentRef.instance === 'object') {
      Object.assign(componentRef.instance, componentProps);
    }
    // put inside the angular component tree
    this.applicationRef.attachView(componentRef.hostView);
    const componentRootNode = (componentRef.hostView as EmbeddedViewRef<
      unknown
    >).rootNodes[0] as HTMLElement;
    // append component to the body
    this.renderer.appendChild(this.document.body, componentRootNode);
    this.componentRef = componentRef;
    return componentRef.instance;
  }

  /**
   * Destroy component
   */
  removeComponent(): void {
    this.applicationRef.detachView(this.componentRef.hostView);
    this.componentRef.destroy();
  }
}

Вариант добавления с помощью CDK Portal

@Injectable({
  providedIn: 'root',
})
export class CdkDomService {
  /** ref to window document slot */
  private bodyPortalOutlet: DomPortalOutlet;

  constructor(
    @Inject(DOCUMENT) document,
    componentFactoryResolver: ComponentFactoryResolver,
    injector: Injector,
    applicationRef: ApplicationRef
  ) {
    this.bodyPortalOutlet = new DomPortalOutlet(
      document.body,
      componentFactoryResolver,
      applicationRef,
      injector
    );
  }

  attachComponent<T>(component: ComponentType<T>, componentProps: object = null): T {
    const componentPortal = new ComponentPortal<T>(component);
    const componentRef = this.bodyPortalOutlet.attach<T>(componentPortal);
    if (componentProps !== null && typeof componentRef.instance === 'object') {
      Object.assign(componentRef.instance, componentProps);
    }
    return componentRef.instance;
  }

  /**
   * Destroy component
   */
  removeComponent(): void {
    this.bodyPortalOutlet.detach();
  }
}

Похожие записи

RxJS Pipeable Operators

Начиная с версии rxjs 5.5 операторы вместо цепочки вызовов применяются как параметры функции pipe.

#local variable внутри *ngIf

Представлены 2 варианта решения, как сослаться на локальную переменную шаблона (#myVar) за пределами шаблона:

  • @ViewChild
  • @ViewChildren
12 февраля 2019 г. в Angular

ngx translate attribute

Используется конструкция

<img src="image.jpg" [alt]="'KEY' | translate"> 
19 августа 2018 г. в Angular

Angular Storybook

Установите пакет npm i @storybook/cli -g и запустите команду sb init в корне angular проекта.

30 октября 2018 г. в Angular

Angular environment service

Использование сервиса для окружения вместо прямой ссылки на environment.ts

26 января 2020 г. в Angular

Вложенные формы Angular

Рассматриваются варианты встраивания форм, позволяющие переиспользовать набор полей вводе со своей логикой. @Input() родительской формы, @Output() дочерней формы, ViewChild(), ControlValueAccessor, ControlContainer