---
title: Angular Modal Service
date: 2017-01-23
tags: Front-End, Angular, Modal
source: https://brianchildress.co/blog/angular-modal-service
---

# Angular Modal Service

As I was starting a new web application project I came across a need for a simple modal. Say what you will about popups or modals they serve a purpose in current web apps. Since my project was all green field, I had the choice of the latest and greatest libraries. I could reach for a simple Bootstrap modal, style it, and call it a day. But I didn't want another library for just one thing, especially something simple. And I quickly started to see how much I would need to override in order to get an off the shelf solution to work with applications style.

So I created a simple modal service, with a little help from my internet friends, thanks [Ben Nadel](https://www.bennadel.com/blog/2806-creating-a-simple-modal-system-in-angularjs.htm), that would be low overhead and provide me with the flexibility my application needed for the long term.

#### _Problem:_

Off the shelf modals = unnecessary and not easily extended; bloated libraries = BAD!

#### _Solution:_

Create a simple modal service that can be used throughout the application for things like communicating error messages, confirmation dialogs, user forms, etc.

#### The Process

To start we need a simple modal component that will contain the global modal styles and functionality. The modal component can then display specific information within the <ng-content> element.

#### _modal.component.ts_

```js
import { Component, HostListener, Input, OnInit } from '@angular/core';
import { ModalService } from './modal.service';

/**
 * ModalComponent - This class represents the modal component.
 * @requires Component
 */

@Component({
  selector: 'app-modal',
  styleUrls: ['app/modal.scss'],
  template: '
  	<div class="modal-container" *ngIf="isOpen">
  	  <div class="modal-overlay" (click)="close(true)"></div>
  	    <div class="app-modal">
  		<div class="title">
		  <h3 *ngIf="modalTitle" [innerHTML]="modalTitle"></h3>
		  <button *ngIf="!blocking && closebtn"
	      	class="btn-close" (click)="close()">X</button>
	    </div>
	    <div class="body">
	      <ng-content></ng-content>
	    </div>
	  </div>
	</div>
  '
})

export class ModalComponent implements OnInit {

  isOpen: boolean = false;

  @Input() closebtn: boolean;
  @Input() modalId: string;
  @Input() modalTitle: string;
  @Input() blocking: boolean;
  @HostListener('document:keyup', ['$event'])
  /**
  * keyup - Checks keys entered for the 'esc' key, attached to hostlistener
  */
  keyup(event: KeyboardEvent): void {
    if (event.keyCode === 27) {
      this.modalService.close(this.modalId, true);
    }
  }

  constructor(private modalService: ModalService) { }

  /**
  * ngOnInit - Initiated when component loads
  */
  ngOnInit() {
    this.modalService.registerModal(this);
  }

  /**
  * close - Closes the selected modal
  */
  close(checkBlocking = false): void {
    this.modalService.close(this.modalId, checkBlocking);
  }

}


```

#### \_modal.scss

Give the modal some basic styles. This will position the modal in the middle of the screen, slightly opaque background. Anything passed into the <ng-content> element can be styled as part of that component to keep code clean and organized.

```css
.modal-container {
  .modal-overlay {
    background: rgba(darkgray, 0.65);
    bottom: 0;
    left: 0;
    position: fixed;
    right: 0;
    top: 0;
    z-index: 1100;
  }
  .app-modal {
    background: white;
    border-radius: 3px;
    left: calc(50% - 250px);
    padding: 0;
    max-height: calc(100% - 100px);
    overflow-y: auto;
    position: fixed;
    top: 50px;
    width: 500px;
    z-index: 1101;
    .title {
      height: 30px;
      h3 {
        display: inline-block;
        margin-top: 0 !important; //to overwrite bootstrap style for h3
      }
      .btn-close {
        background: lightgray;
        border: 0;
        border-radius: 50%;
        float: right;
        height: 25px;
        width: 25px;
      }
    }
  }
}
```

#### _modal.service.ts_

```js
import { Injectable } from '@angular/core';

import { ModalComponent } from './modal.component';

/**
* ModalService - Service used to interact with the Modal Component
*/
@Injectable()
export class ModalService {
  private modals: Array<ModalComponent>;

  constructor() {
    this.modals = [];
  }

  /**
  * close - Closes the selected modal by searching for the component and setting
  * isOpen to false
  * Note: If a modal is set to be 'blocking' a user click outside of the modal will
  * not dismiss the modal, this is off my default
  * @param { String } modalId The id of the modal to close
  */
  close(modalId: string, checkBlocking = false): void {
    let modal = this.findModal(modalId);

    if (modal) {
      if (checkBlocking && modal.blocking) {
        return;
      }
      setTimeout(() => {
        modal.isOpen = false;
      }, 250);

    }
  }

  /**
  * findModal - Locates the specified modal in the modals array
  * @param { String } modalId The id of the modal to find
  */
  findModal(modalId: string): ModalComponent {
    for (let modal of this.modals) {

      if (modal.modalId === modalId) {
        return modal;
      }
    }

    return null;
  }

  /**
  * open - Opens the specified modal based on the suplied modal id
  * @param { String } modalId The id of the modal to open
  */
  open(modalId: string): void {
    let modal = this.findModal(modalId);

    if (modal) {
      setTimeout(() => {
        modal.isOpen = true;
      }, 250);
    }
  }

  /**
  * registerModal - Registers all modal components being used on initialization
  * @param { Object } newModal The new modal to add to the array of available modals
  */
  registerModal(newModal: ModalComponent): void {
    let modal = this.findModal(newModal.modalId);

    // Delete existing to replace the modal
    if (modal) {
      this.modals.splice(this.modals.indexOf(modal), 1);
    }

    this.modals.push(newModal);

  }

}
```

Using the new modal component is easy. We call it within our application:

```html
<app-modal modalId="errorModal" blocking="true" closebtn="true">
  <p>An Error occured, please try this action again.</p>
  ...
</app-modal>
```

And then we can reference that modal from another component.

```js
...

this.modalService.openModal('errorModal');

...
```
