Modals component on github

Modals are streamlined, but flexible, dialog prompts with the minimum required functionality and smart defaults.

The easiest way to add the modals component to your app (will be added to the root module)

Service examples

Open a modal from service.

To be able to open modals from service, inject BsModalService to your constructor.
Then, call .show() method of modal service. Pass a TemplateRef or a component as a first argument and config as a second (optionally).
.show() method returns an instance of BsModalRef class with .hide() method and content property where you'll find a component which you've passed to service.

Template

#

<button type="button" class="btn btn-primary" (click)="openModal(template)">Create template modal</button>

<ng-template #template>
  <div class="modal-header">
    <h4 class="modal-title pull-left">Modal</h4>
    <button type="button" class="btn-close close pull-right" aria-label="Close" (click)="modalRef?.hide()">
      <span aria-hidden="true" class="visually-hidden">&times;</span>
    </button>
  </div>
  <div class="modal-body">
    This is a modal.
  </div>
</ng-template>
import { Component, TemplateRef } from '@angular/core';
import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-modal-service-static',
  templateUrl: './service-template.html',
  standalone: false
})
export class DemoModalServiceStaticComponent {
  modalRef?: BsModalRef;
  constructor(private modalService: BsModalService) {}

  openModal(template: TemplateRef<void>) {
    this.modalRef = this.modalService.show(template);
  }
}

Component

#

Creating a modal with component just as easy as it is with template. Just pass your component in .show() method as in example, and don't forget to include your component to entryComponents of your NgModule
If you passed a component to .show() you can get access to opened modal by injecting BsModalRef. Also you can pass data in your modal by adding initialState field in config. See example for more info

<button type="button" class="btn btn-primary" (click)="openModalWithComponent()">Create modal with component</button>
import { Component, OnInit } from '@angular/core';
import { BsModalRef, BsModalService, ModalOptions } from 'ngx-bootstrap/modal';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-modal-service-component',
  templateUrl: './service-component.html',
  standalone: false
})
export class DemoModalServiceFromComponent {
  bsModalRef?: BsModalRef;
  constructor(private modalService: BsModalService) {}

  openModalWithComponent() {
    const initialState: ModalOptions = {
      initialState: {
        list: ['Open a modal with component', 'Pass your data', 'Do something else', '...'],
        title: 'Modal with component'
      }
    };
    this.bsModalRef = this.modalService.show(ModalContentComponent, initialState);
    this.bsModalRef.content.closeBtnName = 'Close';
  }
}

/* This is a component which we pass in modal*/

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'modal-content',

  template: `
    <div class="modal-header">
      <h4 class="modal-title pull-left">{{ title }}</h4>
      <button type="button" class="btn-close close pull-right" aria-label="Close" (click)="bsModalRef.hide()">
        <span aria-hidden="true" class="visually-hidden">&times;</span>
      </button>
    </div>
    <div class="modal-body">
      <ul *ngIf="list.length">
        <li *ngFor="let item of list">{{ item }}</li>
      </ul>
    </div>
    <div class="modal-footer">
      <button type="button" class="btn btn-default" (click)="bsModalRef.hide()">{{ closeBtnName }}</button>
    </div>
  `,
  standalone: false
})
export class ModalContentComponent implements OnInit {
  title?: string;
  closeBtnName?: string;
  list: string[] = [];

  constructor(public bsModalRef: BsModalRef) {}

  ngOnInit() {
    this.list.push('PROFIT!!!');
  }
}

Nested

#

Nested modals are supported

<button type="button" class="btn btn-primary" (click)="openModal(template)">Open first modal</button>

<ng-template #template>
  <div class="modal-header">
    <h4 class="modal-title pull-left">First modal</h4>
    <button type="button" class="btn-close close pull-right" aria-label="Close" (click)="modalRef?.hide()">
      <span aria-hidden="true" class="visually-hidden">&times;</span>
    </button>
  </div>
  <div class="modal-body">
    This is a first modal<br />
    <button type="button" class="btn btn-primary" (click)="openModal2(templateNested)">Open second modal</button>
    <button type="button" class="btn btn-primary" (click)="closeModal(1)">Close self</button>
    <button type="button" class="btn btn-primary" (click)="closeModal()">Close all modal</button>
  </div>
</ng-template>

<ng-template #templateNested>
  <div class="modal-header">
    <h4 class="modal-title pull-left">Second modal</h4>
    <button type="button" class="btn-close close pull-right" aria-label="Close" (click)="modalRef2?.hide()">
      <span aria-hidden="true" class="visually-hidden">&times;</span>
    </button>
  </div>
  <div class="modal-body">
    This is nested modal.<br />
    <button *ngIf="modalRef" type="button" class="btn btn-danger" (click)="closeFirstModal()">Close first modal</button>
    <button type="button" class="btn btn-danger" (click)="closeModal(2)">Close self</button>
    <button type="button" class="btn btn-danger" (click)="closeModal()">Close all modal</button>
  </div>
</ng-template>
import { Component, TemplateRef } from '@angular/core';
import { BsModalService, BsModalRef } from 'ngx-bootstrap/modal';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-modal-service-nested',
  templateUrl: './service-nested.html',
  standalone: false
})
export class DemoModalServiceNestedComponent {
  modalRef?: BsModalRef | null;
  modalRef2?: BsModalRef;
  constructor(private modalService: BsModalService) {}

  openModal(template: TemplateRef<void>) {
    this.modalRef = this.modalService.show(template, { id: 1, class: 'modal-lg' });
  }
  openModal2(template: TemplateRef<void>) {
    this.modalRef2 = this.modalService.show(template, { id: 2, class: 'second' });
  }
  closeFirstModal() {
    if (!this.modalRef) {
      return;
    }

    this.modalRef.hide();
    this.modalRef = null;
  }
  closeModal(modalId?: number) {
    this.modalService.hide(modalId);
  }
}

Scrolling long content

#


<button type="button" class="btn btn-primary" (click)="openModal(template)">Open modal</button>
<br>

<ng-template #template>
  <div class="modal-header">
    <h4 class="modal-title pull-left">Modal</h4>
    <button type="button" class="btn-close close pull-right" aria-label="Close" (click)="modalRef?.hide()">
      <span aria-hidden="true" class="visually-hidden">&times;</span>
    </button>
  </div>
  <div class="modal-body">
    <p *ngFor="let item of items">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Cumque delectus enim esse excepturi, impedit,
      iste magnam officia optio, quam quis quisquam saepe sint unde velit vitae! Animi in iusto ut?</p>
  </div>
</ng-template>
import { Component, TemplateRef } from '@angular/core';
import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-modal-scrolling-long-content',
  templateUrl: './scrolling-long-content.html',
  standalone: false
})
export class DemoModalScrollingLongContentComponent {
  modalRef?: BsModalRef;
  items: number[];

  constructor(private modalService: BsModalService) {
    this.items = Array(15).fill(0);
  }

  openModal(template: TemplateRef<void>) {
    this.modalRef = this.modalService.show(template);
  }
}

Events

#

Modal service events. Modal service exposes 4 events: onShow, onShown, onHide, onHidden. See usage example below.

onHide and onHidden events emit dismiss reason. Possible values are backdrop-click, esc or {id: number | string} if modal was closed by direct call of hide() method



<button type="button" class="btn btn-primary" (click)="openModal(template)">Open modal</button>
<br><br>
<pre class="card card-block card-header" *ngFor="let message of messages">{{message}}</pre>
<ng-template #template>
  <div class="modal-header">
    <h4 class="modal-title pull-left">Modal</h4>
    <button type="button" class="btn-close close pull-right" aria-label="Close" (click)="modalRef?.hide()">
      <span aria-hidden="true" class="visually-hidden">&times;</span>
    </button>
  </div>
  <div class="modal-body">
    This is a modal
  </div>
</ng-template>
/* eslint-disable @typescript-eslint/no-explicit-any */ // TODO: remove this and fix types
import { ChangeDetectorRef, Component, TemplateRef } from '@angular/core';
import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
import { combineLatest, Subscription } from 'rxjs';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-modal-service-events',
  templateUrl: './service-events.html',
  styles: [
    `
      .card {
        margin-bottom: 0.75rem;
        padding: 8px;
      }
    `
  ],
  standalone: false
})
export class DemoModalServiceEventsComponent {
  modalRef?: BsModalRef;
  subscriptions: Subscription = new Subscription();
  messages: string[] = [];

  constructor(private modalService: BsModalService, private changeDetection: ChangeDetectorRef) {}

  openModal(template: TemplateRef<void>) {
    this.messages = [];

    const _combine = combineLatest([
      this.modalService.onShow,
      this.modalService.onShown,
      this.modalService.onHide,
      this.modalService.onHidden
    ]).subscribe(() => this.changeDetection.markForCheck());

    this.subscriptions.add(
      this.modalService.onShow.subscribe(() => {
        this.messages.push(`onShow event has been fired`);
      })
    );
    this.subscriptions.add(
      this.modalService.onShown.subscribe(() => {
        this.messages.push(`onShown event has been fired`);
      })
    );
    this.subscriptions.add(
      this.modalService.onHide.subscribe((reason: string | any) => {
        if (typeof reason !== 'string') {
          reason = `onHide(), modalId is : ${reason.id}`;
        }
        const _reason = reason ? `, dismissed by ${reason}` : '';
        this.messages.push(`onHide event has been fired${_reason}`);
      })
    );
    this.subscriptions.add(
      this.modalService.onHidden.subscribe((reason: string | any) => {
        if (typeof reason !== 'string') {
          reason = `onHide(), modalId is : ${reason.id}`;
        }
        const _reason = reason ? `, dismissed by ${reason}` : '';
        this.messages.push(`onHidden event has been fired${_reason}`);
        this.unsubscribe();
      })
    );

    this.subscriptions.add(_combine);

    this.modalRef = this.modalService.show(template);
  }

  unsubscribe() {
    this.subscriptions.unsubscribe();
  }
}

Modal ref events. ModalRef exposes 2 events: onHide and onHidden. Note, onShow and onShown are not options because they have already fired by the time the ModalRef is created. See usage example below.

onHide and onHidden events emit dismiss reason. Possible values are backdrop-click, esc or {id: number | string} if modal was closed by direct call of hide() method



<button type="button" class="btn btn-primary" (click)="openModal(template)">Open modal</button>
<br><br>
<pre class="card card-block card-header" *ngFor="let message of messages">{{message}}</pre>
<ng-template #template>
  <div class="modal-header">
    <h4 class="modal-title pull-left">Modal</h4>
    <button type="button" class="btn-close close pull-right" aria-label="Close" (click)="modalRef?.hide()">
      <span aria-hidden="true" class="visually-hidden">&times;</span>
    </button>
  </div>
  <div class="modal-body">
    This is a modal
  </div>
</ng-template>
// @TODO: remove this and fix types
/* eslint-disable @typescript-eslint/no-explicit-any */
import { ChangeDetectorRef, Component, TemplateRef } from '@angular/core';
import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
import { combineLatest, Subscription } from 'rxjs';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-modal-ref-events',
  templateUrl: './modal-ref-events.html',
  styles: [
    `
      .card {
        margin-bottom: 0.75rem;
        padding: 8px;
      }
    `
  ],
  standalone: false
})
export class DemoModalRefEventsComponent {
  modalRef?: BsModalRef;
  subscriptions = new Subscription();
  messages: string[] = [];

  constructor(private modalService: BsModalService, private changeDetection: ChangeDetectorRef) {}

  openModal(template: TemplateRef<void>) {
    this.messages = [];

    this.modalRef = this.modalService.show(template);
    let _combine;
    if (this.modalRef?.onHide && this.modalRef?.onHidden) {
      _combine = combineLatest([this.modalRef.onHide, this.modalRef.onHidden]).subscribe(() =>
        this.changeDetection.markForCheck()
      );
    }

    if (this.modalRef?.onHide) {
      this.subscriptions.add(
        this.modalRef.onHide.subscribe((reason: string | any) => {
          if (typeof reason !== 'string') {
            reason = `onHide(), modalId is : ${reason.id}`;
          }
          const _reason = reason ? `, dismissed by ${reason}` : '';
          this.messages.push(`onHide event has been fired${_reason}`);
        })
      );
    }

    if (this.modalRef?.onHidden) {
      this.subscriptions.add(
        this.modalRef.onHidden.subscribe((reason: string | any) => {
          if (typeof reason !== 'string') {
            reason = `onHide(), modalId is : ${reason.id}`;
          }
          const _reason = reason ? `, dismissed by ${reason}` : '';
          this.messages.push(`onHidden event has been fired${_reason}`);
          this.unsubscribe();
        })
      );
    }

    if (_combine) {
      this.subscriptions.add(_combine);
    }
  }

  unsubscribe() {
    this.subscriptions.unsubscribe();
  }
}

Confirm Window

#

Modal with opportunity to confirm or decline.



<button type="button" class="btn btn-primary" (click)="openModal(template)">Open modal</button>
<br><br>
<pre class="card card-block card-header">{{message}}</pre>
<ng-template #template>
  <div class="modal-body text-center">
    <p>Do you want to confirm?</p>
    <button type="button" class="btn btn-default" (click)="confirm()" >Yes</button>
    <button type="button" class="btn btn-primary" (click)="decline()" >No</button>
  </div>
</ng-template>
import { Component, TemplateRef } from '@angular/core';
import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-modal-service-confirm-window',
  templateUrl: './service-confirm-window.html',
  standalone: false
})
export class DemoModalServiceConfirmWindowComponent {
  modalRef?: BsModalRef;
  message?: string;
  constructor(private modalService: BsModalService) {}

  openModal(template: TemplateRef<void>) {
    this.modalRef = this.modalService.show(template, { class: 'modal-sm' });
  }

  confirm(): void {
    this.message = 'Confirmed!';
    this.modalRef?.hide();
  }

  decline(): void {
    this.message = 'Declined!';
    this.modalRef?.hide();
  }
}

Сustom css class

#

There is possibility to add custom css class to a modal. See the demo below to learn how to use it


<button type="button" class="btn btn-primary" (click)="openModalWithClass(template)">Open modal with custom css class</button>
<br>

<ng-template #template>
  <div class="modal-header">
    <h4 class="modal-title pull-left">Modal</h4>
    <button type="button" class="close btn-close pull-right" aria-label="Close" (click)="modalRef?.hide()">
      <span aria-hidden="true" class="visually-hidden">&times;</span>
    </button>
  </div>
  <div class="modal-body">
    Just a modal with a bunch of words inside, nothing serious.
  </div>
</ng-template>
import { Component, TemplateRef } from '@angular/core';
import { BsModalService, BsModalRef } from 'ngx-bootstrap/modal';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-modal-service-custom-css-class',
  templateUrl: './custom-css-class.html',
  standalone: false
})
export class DemoModalServiceCustomCSSClassComponent {
  modalRef?: BsModalRef;
  constructor(private modalService: BsModalService) {}

  openModalWithClass(template: TemplateRef<void>) {
    this.modalRef = this.modalService.show(template, Object.assign({}, { class: 'gray modal-lg' }));
  }
}

Animation option

#

There is animation option that you can configure.



<button type="button" class="btn btn-primary" (click)="openModal(template)">Open modal</button>
<br>
<br>
<button type="button" class="btn btn-primary btn-sm" (click)="config.animated = !config.animated">{{config.animated ? 'Disable' : 'Enable'}} animation</button>

<ng-template #template>
  <div class="modal-header">
    <h4 class="modal-title pull-left">Modal</h4>
    <button type="button" class="close btn-close pull-right" aria-label="Close" (click)="modalRef?.hide()">
      <span aria-hidden="true" class="visually-hidden">&times;</span>
    </button>
  </div>
  <div class="modal-body">
    Just a modal with a bunch of words inside, nothing serious.
  </div>
</ng-template>
import { Component, TemplateRef } from '@angular/core';
import { BsModalService, BsModalRef } from 'ngx-bootstrap/modal';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-modal-service-disable-animation',
  templateUrl: './disable-animation.html',
  standalone: false
})
export class DemoModalServiceDisableAnimationComponent {
  modalRef?: BsModalRef;
  config = {
    animated: true
  };
  constructor(private modalService: BsModalService) {}

  openModal(template: TemplateRef<void>) {
    this.modalRef = this.modalService.show(template, this.config);
  }
}

Esc closing option

#

There is closing by Esc button option that you can configure.



<button type="button" class="btn btn-primary" (click)="openModal(template)">Open modal</button>
<br>
<br>
<button type="button" class="btn btn-primary btn-sm" (click)="config.keyboard = !config.keyboard">{{config.keyboard ? 'Disable' : 'Enable'}} Esc</button>

<ng-template #template>
  <div class="modal-header">
    <h4 class="modal-title pull-left">Modal</h4>
    <button type="button" class="close btn-close pull-right" aria-label="Close" (click)="modalRef?.hide()">
      <span aria-hidden="true" class="visually-hidden">&times;</span>
    </button>
  </div>
  <div class="modal-body">
    Just a modal with a bunch of words inside, nothing serious.
  </div>
</ng-template>
import { Component, TemplateRef } from '@angular/core';
import { BsModalService, BsModalRef } from 'ngx-bootstrap/modal';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-modal-service-disable-esc-closing',
  templateUrl: './disable-esc-closing.html',
  standalone: false
})
export class DemoModalServiceDisableEscClosingComponent {
  modalRef?: BsModalRef;
  config = {
    keyboard: true
  };
  constructor(private modalService: BsModalService) {}

  openModal(template: TemplateRef<void>) {
    this.modalRef = this.modalService.show(template, this.config);
  }
}

Tooltips and popovers can be placed within modals as needed. When modals are closed, any tooltips and popovers within are also automatically dismissed.

<button type="button" class="btn btn-primary" (click)="openModal(template)">Open modal</button>
<ng-template #template>
  <div class="modal-header">
    <h4 class="modal-title pull-left">Modal</h4>
    <button type="button" class="btn-close close pull-right" aria-label="Close" (click)="modalRef?.hide()">
      <span aria-hidden="true" class="visually-hidden">&times;</span>
    </button>
  </div>
  <div class="modal-body">
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Cumque delectus enim esse excepturi, impedit,
      iste magnam officia optio, quam quis quisquam saepe sint unde velit vitae! Animi in iusto ut?</p>
      <button type="button" class="btn btn-primary" popover="Vivamus sagittis">popover</button>
      <button type="button" class="btn btn-primary" tooltip="Vivamus sagittis">tooltip</button>
  </div>
</ng-template>
import { Component, TemplateRef } from '@angular/core';
import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-modal-with-popups',
  templateUrl: './modal-with-popups.html',
  standalone: false
})
export class DemoModalWithPopupsComponent {
  modalRef?: BsModalRef;

  constructor(private modalService: BsModalService) {}

  openModal(template: TemplateRef<void>) {
    this.modalRef = this.modalService.show(template);
  }
}

Backdrop options

#

There is backdrop options that you can configure.



<button type="button" class="btn btn-primary" (click)="openModal(template)">Open modal</button>
<br>
<br>
<button type="button" class="btn btn-primary btn-sm" (click)="config.backdrop = !config.backdrop">{{config.backdrop ? 'Disable' : 'Enable'}} backdrop</button>
<button type="button" class="btn btn-primary btn-sm" (click)="config.ignoreBackdropClick = !config.ignoreBackdropClick">{{!config.ignoreBackdropClick ? 'Disable' : 'Enable'}} backdrop click</button>

<ng-template #template>
  <div class="modal-header">
    <h4 class="modal-title pull-left">Modal</h4>
    <button type="button" class="close btn-close pull-right" aria-label="Close" (click)="modalRef?.hide()">
      <span aria-hidden="true" class="visually-hidden">&times;</span>
    </button>
  </div>
  <div class="modal-body">
    Just a modal with a bunch of words inside, nothing serious.
  </div>
</ng-template>
import { Component, TemplateRef } from '@angular/core';
import { BsModalService, BsModalRef } from 'ngx-bootstrap/modal';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-modal-service-disable-backdrop',
  templateUrl: './disable-backdrop.html',
  standalone: false
})
export class DemoModalServiceDisableBackdropComponent {
  modalRef?: BsModalRef;
  config = {
    backdrop: true,
    ignoreBackdropClick: false
  };
  constructor(private modalService: BsModalService) {}

  openModal(template: TemplateRef<void>) {
    this.modalRef = this.modalService.show(template, this.config);
  }
}

Change class

#

Calling setClass method to change modal's window class

<button type="button" class="btn btn-primary" (click)="openModal(template)">Create template modal</button>

<ng-template #template>
  <div class="modal-header">
    <h4 class="modal-title pull-left">Modal</h4>
    <button type="button" class="close btn-close pull-right" aria-label="Close" (click)="modalRef?.hide()">
      <span aria-hidden="true" class="visually-hidden">&times;</span>
    </button>
  </div>
  <div class="modal-body">
    This is a modal.
  </div>
  <button type="button" class="btn" (click)="setModalClass()">Change width</button>
</ng-template>
import { Component, TemplateRef } from '@angular/core';
import { BsModalService, BsModalRef } from 'ngx-bootstrap/modal';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-modal-change-class',
  templateUrl: './change-class.html',
  standalone: false
})
export class DemoModalServiceChangeClassComponent {
  modalRef?: BsModalRef;
  valueWidth = false;
  constructor(private modalService: BsModalService) {}

  openModal(template: TemplateRef<void>) {
    this.modalRef = this.modalService.show(template, Object.assign({}, { class: 'modal-sm' }));
  }

  setModalClass() {
    this.valueWidth = !this.valueWidth;
    const modalWidth = this.valueWidth ? 'modal-lg' : 'modal-sm';
    this.modalRef?.setClass(modalWidth);
  }
}

Close interceptor

#

When opening a modal with a component, you can provide an interceptor which will be triggered whenever the modal try to close, allowing you to block the disappearance of a modal.

<button type="button" class="btn btn-primary" (click)="openModalWithInterceptor(template)">Create modal with close interceptor</button>

<ng-template #template>
  <div class="modal-body text-center">
    <p>Do you really want to close?</p>
    <button type="button" class="btn btn-default" (click)="confirm()" >Yes</button>
    <button type="button" class="btn btn-primary" (click)="decline()" >No</button>
  </div>
</ng-template>

import { Component, TemplateRef } from '@angular/core';
import { BsModalService, BsModalRef } from 'ngx-bootstrap/modal';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-modal-service-interceptor',
  templateUrl: './service-interceptor.html',
  standalone: false
})
export class DemoModalServiceWithInterceptorComponent {
  bsModalRef?: BsModalRef;

  confirmModalRef?: BsModalRef;
  confirmResolve?: () => void;
  confirmReject?: () => void;
  confirmPromise?: Promise<void>;

  constructor(private modalService: BsModalService) {}

  openModalWithInterceptor(confirmTemplate: TemplateRef<void>) {
    const closeInterceptor = () => {
      this.confirmPromise = new Promise((resolve, reject) => {
        this.confirmResolve = resolve;
        this.confirmReject = reject;
      });
      this.confirmModalRef = this.modalService.show(confirmTemplate, { class: 'modal-sm' });

      return this.confirmPromise;
    };
    this.bsModalRef = this.modalService.show(ModalContentWithInterceptorComponent, { closeInterceptor });
    this.bsModalRef.content.closeBtnName = 'Close';
  }

  confirm(): void {
    if (this.confirmResolve) {
      this.confirmResolve();
    }
    this.confirmModalRef?.hide();
  }

  decline(): void {
    if (this.confirmReject) {
      this.confirmReject();
    }
    this.confirmModalRef?.hide();
  }
}

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'modal-content-with-interceptor',
  template: `
    <div class="modal-header">
      <h4 class="modal-title pull-left">Modal with interceptor</h4>
      <button type="button" class="close btn-close pull-right" aria-label="Close" (click)="bsModalRef?.hide()">
        <span aria-hidden="true" class="visually-hidden">&times;</span>
      </button>
    </div>
    <div class="modal-body">This modal has closing interceptor</div>
    <div class="modal-footer">
      <button type="button" class="btn btn-default" (click)="bsModalRef?.hide()">Close</button>
    </div>
  `,
  standalone: false
})
export class ModalContentWithInterceptorComponent {
  constructor(public bsModalRef: BsModalRef) {}
}

Directive examples

Also you can use directive instead of service. See the demos below

Static modal

#

<button type="button" class="btn btn-primary" (click)="staticModal.show()">Static modal</button>

<div class="modal fade" bsModal #staticModal="bs-modal" [config]="{backdrop: 'static'}"
     tabindex="-1" role="dialog" aria-labelledby="dialog-static-name">
  <div class="modal-dialog modal-sm">
    <div class="modal-content">
      <div class="modal-header">
        <h4 id="dialog-static-name" class="modal-title pull-left">Static modal</h4>
        <button type="button" class="btn-close close pull-right" aria-label="Close" (click)="staticModal.hide()">
          <span aria-hidden="true" class="visually-hidden">&times;</span>
        </button>
      </div>
      <div class="modal-body">
        This is static modal, backdrop click will not close it.
        Click <b>&times;</b> to close modal.
      </div>
    </div>
  </div>
</div>
import { Component } from '@angular/core';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-modal-static',
  templateUrl: './static.html',
  standalone: false
})
export class DemoModalStaticComponent {}

Optional sizes

#

Small modal window have small width on screens only above 768px(boostrap3) and 576px(bootstrap4)

<!--Large modal-->
<p>Small modal window have small width on screens only above 768px(boostrap3) and 576px(bootstrap4)</p>
<button class="btn btn-primary" (click)="lgModal.show()">Large modal</button>

<div bsModal #lgModal="bs-modal" class="modal fade" tabindex="-1"
     role="dialog" aria-labelledby="dialog-sizes-name1">
  <div class="modal-dialog modal-lg">
    <div class="modal-content">
      <div class="modal-header">
        <h4 id="dialog-sizes-name1" class="modal-title pull-left">Large modal</h4>
        <button type="button" class="btn-close close pull-right" (click)="lgModal.hide()" aria-label="Close">
          <span aria-hidden="true" class="visually-hidden">&times;</span>
        </button>
      </div>
      <div class="modal-body">
        ...
      </div>
    </div>
  </div>
</div>


<!--Small modal-->
<button type="button" class="btn btn-primary" (click)="smModal.show()">Small modal</button>

<div bsModal #smModal="bs-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="dialog-sizes-name2">
  <div class="modal-dialog modal-sm">
    <div class="modal-content">
      <div class="modal-header">
        <h4 id="dialog-sizes-name2" class="modal-title pull-left">Small modal</h4>
        <button type="button" class="btn-close close pull-right" aria-label="Close" (click)="smModal.hide()">
          <span aria-hidden="true" class="visually-hidden">&times;</span>
        </button>
      </div>
      <div class="modal-body">
        ...
      </div>
    </div>
  </div>
</div>
import { Component } from '@angular/core';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-modal-sizes',
  templateUrl: './sizes.html',
  standalone: false
})
export class DemoModalSizesComponent {}

Child modal

#

Control modal from parent component

<button type="button" class="btn btn-primary" (click)="showChildModal()">Open child modal</button>
<div bsModal #childModal="bs-modal" class="modal fade" tabindex="-1"
     role="dialog" aria-labelledby="dialog-child-name">
  <div class="modal-dialog modal-sm">
    <div class="modal-content">
      <div class="modal-header">
        <h4 id="dialog-child-name" class="modal-title pull-left">Child modal</h4>
        <button type="button" class="close pull-right btn-close" aria-label="Close" (click)="hideChildModal()">
          <span aria-hidden="true" class="visually-hidden">&times;</span>
        </button>
      </div>
      <div class="modal-body">
        I am a child modal, opened from parent component!
      </div>
    </div>
  </div>
</div>
import { Component, ViewChild } from '@angular/core';
import { ModalDirective } from 'ngx-bootstrap/modal';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-modal-child',
  templateUrl: './child.html',
  standalone: false
})
export class DemoModalChildComponent {
  @ViewChild('childModal', { static: false }) childModal?: ModalDirective;

  showChildModal(): void {
    this.childModal?.show();
  }

  hideChildModal(): void {
    this.childModal?.hide();
  }
}

Nested modals

#

Open a modal from another modal

<button type="button" class="btn btn-primary" (click)="parentModal.show()">Open parent modal</button>
<div class="modal fade" bsModal #parentModal="bs-modal" tabindex="-1" role="dialog" aria-labelledby="dialog-nested-name1">
  <div class="modal-dialog modal-lg">
    <div class="modal-content">
      <div class="modal-header">
        <h4 id="dialog-nested-name1" class="modal-title pull-left">First modal</h4>
        <button type="button" class="btn-close close pull-right" aria-label="Close" (click)="parentModal.hide()">
          <span aria-hidden="true" class="visually-hidden">&times;</span>
        </button>
      </div>
      <div class="modal-body">
        <button type="button" class="btn btn-primary" (click)="childModal.show()">Open second modal</button>
      </div>
    </div>
  </div>
</div>

<div class="modal fade" bsModal #childModal="bs-modal" tabindex="-1" role="dialog" aria-labelledby="dialog-nested-name2">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h4 id="dialog-nested-name2" class="modal-title pull-left">Second modal</h4>
        <button type="button" class="btn-close close pull-right" aria-label="Close" (click)="childModal.hide()">
          <span aria-hidden="true" class="visually-hidden">&times;</span>
        </button>
      </div>
      <div class="modal-body">
        This is second modal <br>
        <button type="button" class="btn btn-primary" (click)="thirdModal.show()">Open third modal</button>
      </div>
    </div>
  </div>
</div>

<div class="modal fade" bsModal #thirdModal="bs-modal" tabindex="-1" role="dialog" aria-labelledby="dialog-nested-name3">
  <div class="modal-dialog modal-sm">
    <div class="modal-content">
      <div class="modal-header">
        <h4 id="dialog-nested-name3" class="modal-title pull-left">Third modal</h4>
        <button type="button" class="btn-close close pull-right" aria-label="Close" (click)="thirdModal.hide()">
          <span aria-hidden="true" class="visually-hidden">&times;</span>
        </button>
      </div>
      <div class="modal-body">
        This is third modal <br>
        Click <b>&times;</b> to close modal.
      </div>
    </div>
  </div>
</div>
import { Component } from '@angular/core';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-modal-nested',
  templateUrl: './nested.html',
  standalone: false
})
export class DemoModalNestedComponent {}

Modal events

#

ModalDirective exposes 4 events: onShow, onShown, onHide, onHidden. See usage example below.
$event is an instance of ModalDirective. There you may find some useful properties like isShown, dismissReason, etc.
For example, you may want to know which one of user's actions caused closing of a modal. Just get the value of dismissReason,
possible values are backdrop-click, esc or null if modal was closed by direct call of hide() method



<button type="button" class="btn btn-primary" (click)="showModal()">Open a modal</button>
<br><br>
<pre class="card card-block card-header" *ngFor="let message of messages">{{message}}</pre>

<div class="modal fade" bsModal #modal="bs-modal"
     tabindex="-1" role="dialog" aria-labelledby="dialog-events-name"
     (onShow)="handler('onShow', $event)"
     (onShown)="handler('onShown', $event)"
     (onHide)="handler('onHide', $event)"
     (onHidden)="handler('onHidden', $event)">
  <div class="modal-dialog modal-sm">
    <div class="modal-content">
      <div class="modal-header">
        <h4 id="dialog-events-name" class="modal-title pull-left">Modal</h4>
        <button type="button" class="close pull-right btn-close" aria-label="Close" (click)="modal.hide()">
          <span aria-hidden="true" class="visually-hidden">&times;</span>
        </button>
      </div>
      <div class="modal-body">
        Just another modal <br>
        Click <b>&times;</b>, press <code>Esc</code> or click on backdrop to close modal.
      </div>
    </div>
  </div>
</div>
import { Component, ViewChild } from '@angular/core';
import { ModalDirective } from 'ngx-bootstrap/modal';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-modal-events',
  templateUrl: './events.html',
  styles: [`
    .card {
      margin-bottom: 0.75rem;
      padding: 8px;
    }
  `],
  standalone: false
})
export class DemoModalEventsComponent {
  @ViewChild(ModalDirective, { static: false }) modal?: ModalDirective;
  messages?: string[];

  showModal() {
    this.messages = [];
    this.modal?.show();
  }
  handler(type: string, $event: ModalDirective) {
    this.messages?.push(
      `event ${type} is fired${$event.dismissReason
        ? ', dismissed by ' + $event.dismissReason
        : ''}`
    );
  }
}

Auto shown modal

#

Show modal right after it has been initialized. This allows you to keep DOM clean by only appending visible modals to the DOM using *ngIf directive.

It can also be useful if you want your modal component to perform some initialization operations, but want to defer that until user actually sees modal content. I.e. for a "Select e-mail recipient" modal you might want to defer recipient list loading until the modal is shown.

<button type="button" class="btn btn-primary" (click)="showModal()">Render auto-shown modal</button>
<div *ngIf="isModalShown" [config]="{ show: true }" (onHidden)="onHidden()" bsModal #autoShownModal="bs-modal"
     class="modal fade" tabindex="-1" role="dialog" aria-labelledby="dialog-auto-name">
  <div class="modal-dialog modal-sm">
    <div class="modal-content">
      <div class="modal-header">
        <h4 id="dialog-auto-name" class="modal-title pull-left">Auto shown modal</h4>
        <button type="button" class="btn-close close pull-right" aria-label="Close" (click)="hideModal()">
          <span aria-hidden="true" class="visually-hidden">&times;</span>
        </button>
      </div>
      <div class="modal-body">
        <p>I am a modal that is shown right after initialization!</p>
        <p>I wasn't present in DOM until you clicked the button</p>
        <p>When you close me, I'll be removed from the DOM</p>
      </div>
    </div>
  </div>
</div>
import { Component, ViewChild } from '@angular/core';
import { ModalDirective } from 'ngx-bootstrap/modal';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-modal-auto-shown',
  templateUrl: './auto-shown.html',
  standalone: false
})
export class DemoAutoShownModalComponent {
  @ViewChild('autoShownModal', { static: false }) autoShownModal?: ModalDirective;
  isModalShown = false;

  showModal(): void {
    this.isModalShown = true;
  }

  hideModal(): void {
    this.autoShownModal?.hide();
  }

  onHidden(): void {
    this.isModalShown = false;
  }
}

Accessibility

#

Be sure to add id="" attribute to your title and description in the template to make your modal works according to accessibility. The aria-labelledby attribute establishes relationships between the modal and its title (only if the title has id attribute). The element containing the modal's description is referenced by aria-describedby attribute. The dialog does not need aria-describedby since there is no static text that describes it.

Use modal options to set aria-labelledby and aria-describedby attributes.

<button type="button" class="btn btn-primary" (click)="openModal(template)">Create template modal</button>

<ng-template #template>
  <div class="modal-header">
    <h4 class="modal-title pull-left" id="my-modal-title">Modal title</h4>
    <button type="button" class="btn-close close pull-right" aria-label="Close" (click)="modalRef?.hide()">
      <span aria-hidden="true" class="visually-hidden">&times;</span>
    </button>
  </div>
  <div class="modal-body">
    <div id="my-modal-description">
      This is a modal.
    </div>
  </div>
</ng-template>
import { Component, TemplateRef } from '@angular/core';
import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-accessibility',
  templateUrl: './accessibility.html',
  standalone: false
})
export class DemoAccessibilityComponent {
  modalRef?: BsModalRef;
  constructor(private modalService: BsModalService) {}

  openModal(template: TemplateRef<void>) {
    this.modalRef = this.modalService.show(template, {
      ariaDescribedby: 'my-modal-description',
      ariaLabelledBy: 'my-modal-title'
    });
  }
}

Installation

ng add ngx-bootstrap  --component modals
### Standalone component usage
import { ModalModule, BsModalService } from 'ngx-bootstrap/modal';

@Component({
  standalone: true,
  imports: [ModalModule,...], // module can be optional
  providers: [BsModalService]
})
export class AppComponent(){}

### Module usage
import { ModalModule } from 'ngx-bootstrap/modal';

@NgModule({
  imports: [ModalModule,...],
  providers: [BsModalService]
})
export class AppModule(){}

Mark any code with directive to show it's content in modal

Selector

This component will be added as background layout for modals if enabled

Selector

Template

Component

Nested

Scrolling long content


Events





Confirm Window



Сustom css class


Animation option



Esc closing option



Backdrop options



Change class

Close interceptor

Static modal

Optional sizes

Small modal window have small width on screens only above 768px(boostrap3) and 576px(bootstrap4)

Child modal

Nested modals

Modal events



Auto shown modal