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

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

let-* $implicit in Angular template

Синтаксис let-* позволяет объявить переменную в шаблоне , использования ключа $implicit позволяет устанавливать значение по-умолчанию для объявленной переменной.

29 августа 2018 г. в Angular

RxJs Subjects

Выдержки из доклада Андрея Алексеева (Tinkoff) про RxJs (Subject, Behaviour Subject, Replay Subject, Async Subject). Применение в Angular.

Angular Storybook

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

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