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 Subjects

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

@Attribute() декоратор

Аналогично @Input() позволяет получить значение атрибута с хоста компонента/директивы, но не отслеживает дальнейшее изменение атрибута.

14 сентября 2019 г. в Angular

Angular routerLink conditionally

<a [routerLink]="myVar ? '/home' : null" routerLinkActive="is-active">Home</a>
or
<a [routerLink]="myVar ? ['/home'] : []">Home</a>
02 сентября 2019 г. в Angular

Angular Resolver

Resolver гарантированно получает асинхронные данные до создания компонента исходя из параметров маршрута.

Angular. Отличие baseHref от deployUrl

  • deployUrl - задаёт путь для статических (js, css) файлов в index.html.
  • baseHref - определяет base, используется в ссылках и маршрутизации (routing) Angular