0

In an Angular 7 modal service I am creating a modal Stackblitz.

The modal is simply a DIV added to document as follows:

const modal = document.createElement('div');
modal.classList.add('modal');
modal.appendChild(this.element);

document.body.appendChild(this.modal);

The modal content (this.element) is a component created dynamically.

What I would like is modal to be also a Component added dynamically:

  1. Create modal from a ModalComponent (I think similar to 2);
  2. Create element component (DONE);
  3. Add element component as child of modal component;
  4. Add modal component to document.

Could someone help me with (3) and (4)? Not sure how to do it.

Modal

export class Modal {
  protected modal: any = null;
  close() {
    this.modal.close();
  }
}

ModalService

import { ApplicationRef, ComponentFactoryResolver, EmbeddedViewRef, Injectable, Injector, ComponentRef, ReflectiveInjector } from '@angular/core';

@Injectable({
  providedIn: 'root'
})

export class ModalService {

  private component: ComponentRef<any>;
  private element: any;
  private stage: any;

  constructor(private componentFactoryResolver: ComponentFactoryResolver, private application: ApplicationRef, private injector: Injector) { }

  open(component: any, data: any): void {

    if(this.element) 
      return;

    const injector: Injector = ReflectiveInjector.resolveAndCreate([{ provide: 'modal', useValue: data }]);

    this.component = this.componentFactoryResolver.resolveComponentFactory(component).create(injector);

    this.component.instance.modal = this;

    this.application.attachView(this.component.hostView);

    this.element = (this.component.hostView as EmbeddedViewRef<any>).rootNodes[0] as HTMLElement;

    const modal = document.createElement('div');
    modal.classList.add('modal');
    modal.appendChild(this.element);

    document.body.appendChild(this.modal);

  }

  close(): void {

    this.application.detachView(this.component.hostView);
    this.stage.parentNode.removeChild(this.stage);
    this.component.destroy();
    this.element = null;

  }

}
Miguel Moura
  • 36,732
  • 85
  • 259
  • 481
  • Can you please update your stackblitz link? – yurzui Apr 18 '19 at 10:18
  • Done ... I deleted the link by mistake. – Miguel Moura Apr 18 '19 at 10:21
  • Sorry, but where is the ModalComponent in your example? I only see HelloComponent(which is element I suppose) – yurzui Apr 18 '19 at 10:25
  • Maybe you're looking for something like this https://stackoverflow.com/questions/44284026/creating-a-angular2-component-with-ng-content-dynamically – yurzui Apr 18 '19 at 10:29
  • @yurzui I just added the Modal.component.ts, Modal.component.html and Modal.component.css. What I am looking for is ModalComponent to be used as parent of the, in the case, HelloComponent and added to the document. Instead of using document.createelement – Miguel Moura Apr 18 '19 at 10:37
  • @yurzui I checked that question and in fact I tried something of that but I wasn't able to make it work ... But to be honest I am not completely sure of the path to do it. – Miguel Moura Apr 18 '19 at 10:39

1 Answers1

3

First you need to dynamically create that inner component and then use projectableNodes parameter in ComponentFactory.create method to project it into your dynamically created ModalComponent.

The ModalComponent is supposed to have ng-content tag in its template:

<div class="stage">
  <div class="modal">
    <ng-content></ng-content>
  </div>
</div>

modal.service.ts

open(component: Type<Modal>): void {
  if (this.modalRef)
    return;

  this.elementRef = this.componentFactoryResolver
    .resolveComponentFactory(component)
    .create(this.injector);
  this.appRef.attachView(this.elementRef.hostView);
  this.elementRef.instance.modal = this;

  this.modalRef = this.componentFactoryResolver
    .resolveComponentFactory(ModalComponent)
    .create(this.injector, [[this.elementRef.location.nativeElement]]);
                                         \/
                      here we're projecting inner component into modal

  this.appRef.attachView(this.modalRef.hostView);

  document.body.appendChild(this.modalRef.location.nativeElement);
}


close(): void {
  this.appRef.detachView(this.elementRef.hostView);
  this.elementRef.destroy();
  this.elementRef = null;

  this.appRef.detachView(this.modalRef.hostView);
  this.modalRef.destroy();
  this.modalRef = null;
}

Forked Stackblitz

yurzui
  • 205,937
  • 32
  • 433
  • 399