Angular 6 - Custom Modal Window / Dialog Box
Built with Angular 6.1.10 and Webpack 4.30.0
Other versions available:
- Angular: Angular 14, 10, 9, 8, 7, 2/5
- React: React
- AngularJS: AngularJS
- Vanilla JS: Vanilla JS
In this tutorial we'll cover how to implement modal windows (dialog boxes) in Angular 6 with TypeScript. The example is a custom modal without the need for any 3rd party libraries.
The tutorial code is available on GitHub at https://github.com/cornflourblue/angular-6-custom-modal.
There are plenty of 3rd party libraries and plugins out there that include modal windows, and up until recently I used them myself when I needed to add a modal to a project. The main issue that I have with 3rd party libraries is that they usually contain a lot of features I don't need which adds unnecessary bloat to my application, so a while back I took the time to create a custom modal window in Angular 1 to see how difficult it would be and also to remove the mystery I had in my mind about exactly how modals work.
Once I'd finished I was surprised at the relatively small amount of code required to implement a custom modal window, most of the modal 'magic' is done with a handful of css styles (see the modal.less file) while TypeScript is just used for showing and hiding the modal windows.
Here it is in action: (See on StackBlitz at https://stackblitz.com/edit/angular-6-custom-modal-dialog)
- 16 Apr 2019 - Updated to Angular 6.1.10 and Webpack 4.30.0
- 25 May 2018 - Built with Angular 6.0.3 and Webpack 4.8.1
Running the Angular 6 Modal Example Locally
- Install NodeJS and NPM from https://nodejs.org/en/download/.
- Download or clone the project source code from https://github.com/cornflourblue/angular-6-custom-modal
- Install all required npm packages by running
npm install
from the command line in the project root folder (where the package.json is located). - Start the application by running
npm start
from the command line in the project root folder.
Angular 6 Custom Modal Setup
There are three main pieces of code that make up this custom modal implementation:
- Modal LESS/CSS styles in
/src/app/_content/modal.less
- Angular 6 Modal Service in
/src/app/_services/modal.service.ts
- Angular 6 Modal Directive in
/src/app/_directives/modal.component.ts
Adding an Angular 6 Modal Dialog to your page
Once you have the above three files (or the code from them) integrated into your project, you can add modals to your pages with the custom <jw-modal>
tag like this:
<jw-modal id="custom-modal-1">
<h1>A Custom Modal!</h1>
<p>Home page text: <input type="text" [(ngModel)]="bodyText" /></p>
<button (click)="closeModal('custom-modal-1');">Close</button>
</jw-modal>
A unique id is required for each modal on a page, this is passed to the modal service from your controller to identify which modal to open/close.
You can put any content you want within the <jw-modal>
element, you could also update the modal LESS/CSS if you want to change the styles of the modals, e.g to make them smaller or add CSS transitions etc. By default modals are closed on background click, to disable this remove the chunk of code in modal.component.ts
located directly below the comment // close modal on background click
.
Opening & Closing Angular 6 Modal Dialogs
To open and close (show/hide) a modal dialog you need to use an instance of the ModalService by importing it into your controller, injecting it into the constructor and calling this.modalService.open()
and this.modalService.close()
, e.g:
import { Component, OnInit } from '@angular/core';
import { ModalService } from '../_services';
@Component({ templateUrl: 'home.component.html' })
export class HomeComponent implements OnInit {
private bodyText: string;
constructor(private modalService: ModalService) {
}
ngOnInit() {
this.bodyText = 'This text can be updated in modal 1';
}
openModal(id: string) {
this.modalService.open(id);
}
closeModal(id: string) {
this.modalService.close(id);
}
}
The example controller passes the modal id from the view, but it could also be set directly in the controller like this:
this.modalService.open('my-custom-modal');
The view then calls the methods exposed by the controller in the usual way:
<div class="col-md-6 col-md-offset-3">
<h1>Home</h1>
<p>{{bodyText}}</p>
<button (click)="openModal('custom-modal-1')">Open Modal 1</button>
<button (click)="openModal('custom-modal-2')">Open Modal 2</button>
</div>
Breakdown of the Angular 6 Custom Modal Code
Below is a breakdown of the pieces of code used to implement custom modal dialogs in Angular 6, you don't need to know the details of how it all works to use the modals in your project, it's only if you're interested in the nuts and bolts or if you want to modify the code or behaviour.
LESS/CSS Styles for Angular 6 Modal Dialogs
These are the styles applied to the custom modal dialogs in this example, they could also be used in non-angular projects as it's just pure LESS/CSS.
I prefixed the modal element and classes with jw-
to prevent conflicts with 3rd party css libraries such as Bootstrap.
/* MODAL STYLES
-------------------------------*/
jw-modal {
/* modals are hidden by default */
display: none;
.jw-modal {
/* modal container fixed across whole screen */
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
/* z-index must be higher than .jw-modal-background */
z-index: 1000;
/* enables scrolling for tall modals */
overflow: auto;
.jw-modal-body {
padding: 20px;
background: #fff;
/* margin exposes part of the modal background */
margin: 40px;
}
}
.jw-modal-background {
/* modal background fixed across whole screen */
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
/* semi-transparent black */
background-color: #000;
opacity: 0.75;
/* z-index must be below .jw-modal and above everything else */
z-index: 900;
}
}
body.jw-modal-open {
/* body overflow is hidden to hide main scrollbar when modal window is open */
overflow: hidden;
}
Angular 6 Modal Service
The modal service manages the communication that's required between angular 6 controllers and modal directive instances. It maintains a current list of available modals on the page and exposes methods for interacting with those modals.
export class ModalService {
private modals: any[] = [];
add(modal: any) {
// add modal to array of active modals
this.modals.push(modal);
}
remove(id: string) {
// remove modal from array of active modals
this.modals = this.modals.filter(x => x.id !== id);
}
open(id: string) {
// open modal specified by id
let modal: any = this.modals.filter(x => x.id === id)[0];
modal.open();
}
close(id: string) {
// close modal specified by id
let modal: any = this.modals.filter(x => x.id === id)[0];
modal.close();
}
}
Angular 6 Modal Directive Component
The custom modal directive is used for adding modals anywhere in your angular application by using the <jw-modal>
tag. Each modal instance registers itself with the ModalService when it loads using the ngOnInit
lifecycle hook, and removes itself from the ModalService when it's destroyed using the ngOnDestroy
lifecycle hook.
import { Component, ElementRef, Input, OnInit, OnDestroy } from '@angular/core';
import { ModalService } from '../_services';
@Component({
selector: 'jw-modal',
template:
`<div class="jw-modal">
<div class="jw-modal-body">
<ng-content></ng-content>
</div>
</div>
<div class="jw-modal-background"></div>`
})
export class ModalComponent implements OnInit, OnDestroy {
@Input() id: string;
private element: any;
constructor(private modalService: ModalService, private el: ElementRef) {
this.element = el.nativeElement;
}
ngOnInit(): void {
let modal = this;
// ensure id attribute exists
if (!this.id) {
console.error('modal must have an id');
return;
}
// move element to bottom of page (just before </body>) so it can be displayed above everything else
document.body.appendChild(this.element);
// close modal on background click
this.element.addEventListener('click', function (e: any) {
if (e.target.className === 'jw-modal') {
modal.close();
}
});
// add self (this modal instance) to the modal service so it's accessible from controllers
this.modalService.add(this);
}
// remove self from modal service when directive is destroyed
ngOnDestroy(): void {
this.modalService.remove(this.id);
this.element.remove();
}
// open modal
open(): void {
this.element.style.display = 'block';
document.body.classList.add('jw-modal-open');
}
// close modal
close(): void {
this.element.style.display = 'none';
document.body.classList.remove('jw-modal-open');
}
}
Need Some Angular 6 Help?
Search fiverr for freelance Angular 6 developers.
Follow me for updates
When I'm not coding...
Me and Tina are on a motorcycle adventure around Australia.
Come along for the ride!