1
votes

I am trying to make a reusable quickView modal in my app to load any component dynamically using the ng-bootstrap modal library

It's working fine as far I am loading the same example component as shown in docs, but not for the components I created to test.

How can I use the quickView modal for dynamic components creation to load in modal-body?

https://stackblitz.com/edit/angular-quickview

I am using simple if/else to open a component in the modal based on name string.

<button class="btn btn-lg btn-outline-secondary mr-2" (click)="open('default')">Launch default</button>
<button class="btn btn-lg btn-outline-danger mr-2" (click)="open('red')">Launch red</button>
<button class="btn btn-lg btn-outline-primary" (click)="open('blue')">Launch blue</button>
open(name: string) {
    if (name === "default") {
      const modalRef = this.modalService.open(NgbdModalContent);
      modalRef.componentInstance.name = "Default";
    } else if (name === "red") {
      const modalRef = this.modalService.open(RedComponent);
      modalRef.componentInstance.name = "Red";
    } else if (name === "blue") {
      const modalRef = this.modalService.open(BlueComponent);
      modalRef.componentInstance.name = "Blue";
    }
  }

I also tried with componentFactoryResolver to inject my component into modal-body but that also throw error Error: Cannot read property 'viewContainerRef' of undefined

 <div class="modal-body">
      <ng-template quickView></ng-template>
 </div>
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(
        RedComponent
      );
      const viewContainerRef = this.quickView.viewContainerRef;
      viewContainerRef.clear();

      const componentRef = viewContainerRef.createComponent<any>(
        componentFactory
      );
      componentRef.instance.name = "Red";
2

2 Answers

3
votes

You can try adding modal service so it can resolveComponentFactory custom components and in that case the modal will be singleton.

modal.service.ts

import { Injectable, ComponentFactoryResolver } from "@angular/core";
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
@Injectable()
export class ModalService {
  constructor(
    private ngbModal: NgbModal,
    private componentFactoryResolver: ComponentFactoryResolver
  ) {}

  showDefaultModalComponent(theComponent: any, name: any) {
    const componenetFactory = this.componentFactoryResolver.resolveComponentFactory(
      theComponent
    );
    const modalRef = this.ngbModal.open(theComponent);
    modalRef.componentInstance.name = name;
    return modalRef;
  }

  showFeaturedDialog(theComponent: any, name: any) {
    const componenetFactory = this.componentFactoryResolver.resolveComponentFactory(
      theComponent
    );

    const modalRef = this.ngbModal.open(theComponent);
    modalRef.componentInstance.name = name;
    return modalRef;
  }
}

And then inject that service into the main component.

export class NgbdModalComponent {
  constructor(private customModal: ModalService) {}

  open(name: string) {
    if (name === "default") {
      this.customModal.showDefaultModalComponent(NgbdModalContent, "Default");
    } else if (name === "red") {
      this.customModal.showFeaturedDialog(RedComponent, "Red");
    } else if (name === "blue") {
      this.customModal.showFeaturedDialog(BlueComponent, "Blue");
    }
  }
}

In case you want to use quickView directive please check that the reason why your example is not working is in RedComponent and BlueComponent. You have added twice @Component decorator as seen on the example bellow:

enter image description here

Please see the edited and working Stackblitz example:

https://stackblitz.com/edit/angular-quickview-ykwz4i?file=src/app/modal-component.ts

Note: Entry components in Angular 11 are deprecitated.

-1
votes

You can inject the components dynamically in modal by declaring those components in entryComponents in your app module as follows.

    @NgModule({
    declarations: [
        AppComponent,
        RedComponent,
        BlueComponent
    ],
    imports: [
        BrowserModule,
        FormsModule,
        BrowserAnimationsModule,
    ],
    providers: [],
    entryComponents: [RedComponent, BlueComponent],
    bootstrap: [AppComponent]
  })
  export class AppModule { }