How to use Bootstrap Modals in Angular in separate components

As many might have experienced, some controls from Bootstrap cannot be easily used in Angular and that's why a good solution is: ng-Bootstrap.

npm install --save @ng-bootstrap/ng-bootstrap

However, not all controls are so easy to use and one complex situation happens when you want to split Modals into multiple components. The regular Modal looks like this:

HTML Code:

<a (click)="openModal(contentModal)" class="nav-link">Open modal</a>
<ng-template #contentModal let-c="close" let-d="dismiss">
<div class="modal-header">
<h4 class="modal-title" id="modal-primary-title">Title</h4>
<button type="button" class="close" aria-label="Close" (click)="d('Cross click')">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body centered">
<!--Body-->
</div>
</ng-template>

TypeScript code:

openModal(contentModal) {
    this.modalService.open(contentModal);
}

And it's perfectly fine if you have one or two Modals and they don't have any logic behind, them besides popping up as in an About Modal (Figure 1), but what if you have 3+ Modals with full logic?

Figure 1. Modal.

These are often business requirements, which could create a big mess in your code. The previous solution is not feasible at all in this case. And let's not talk about scalability or many other related examples.

After many attempts, I designed a solution that helped split the Modals into independent components.

The first step is to create your new component:

ng generate component ModalComponent

After, register your Modal in the entryComponents section of your app.module.ts (before Angular 16):

entryComponents: [
    ModalComponent,
],

Next, copy your old Modal to your new component and remove these 2 lines:

<ng-template #contentModal let-c="close" let-d="dismiss">
</ng-template>

It should look like this:

<div class="modal-header">
<h4 class="modal-title" id="modal-primary-title">Title</h4>
<button type="button" class="close" aria-label="Close" (click)="d('Cross click')">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body centered">
<!--Body-->
</div>

Now, comes a small change in the logic of the click event and how to call it:

This is the new HTML code:

<a class="nav-link" (click)="openModal()">About</a>

This is the new TypeScript event:
 
openModal() {
//Here you define the name of your component
this.modalService.open(ModalComponent);
//This section is if you want to have any variable to initialize
//compConst.componentInstance.weight = undefined;
}

Also, in your new Component, you must change your constructor and add an instance of NgbActiveModal.

constructor(public activeModal: NgbActiveModal) { }

Another important change is in the logic of closing the modal, it needs to be changed to this:

<button type="button" class="close" aria-label="Close"
(click)="activeModal.dismiss('Cross click')">
<span aria-hidden="true">&times;</span>
</button>

You need to change the old: (click)="d('Cross click')" to (click)="activeModal.dismiss('Cross click')"

And with all these changes it will work like a charm. I got some inspiration from here:

Comments