Creating a Custom Modal Service in Angular for Reusable Dialogs

Creating a custom modal service in Angular allows developers to implement reusable dialog components that enhance user experience and maintainability. This guide walks you through building a flexible modal service from scratch.

Understanding the Need for a Custom Modal Service

In Angular applications, dialogs and modals are common UI elements used for confirmations, forms, or alerts. While Angular Material provides built-in dialog components, customizing your own modal service offers greater flexibility and control over appearance and behavior.

Implementing the Modal Service

Start by creating a new service that manages the opening and closing of modal components. This service will handle dynamic component loading and communication between components.

Creating the Modal Service

Generate a new service using Angular CLI:

ng generate service services/modal

Next, implement methods to open and close modals, utilizing Angular’s ComponentFactoryResolver and ViewContainerRef for dynamic component loading.

Sample Modal Service Code

import { Injectable, ComponentRef, ComponentFactoryResolver, ApplicationRef, Injector } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class ModalService {
  private modalRef: ComponentRef | null = null;

  constructor(
    private resolver: ComponentFactoryResolver,
    private appRef: ApplicationRef,
    private injector: Injector
  ) {}

  open(component: any, data?: any): void {
    if (this.modalRef) {
      this.close();
    }
    const factory = this.resolver.resolveComponentFactory(component);
    this.modalRef = factory.create(this.injector);
    if (data) {
      Object.assign(this.modalRef.instance, data);
    }
    this.appRef.attachView(this.modalRef.hostView);
    const domElem = (this.modalRef.hostView as any).rootNodes[0] as HTMLElement;
    document.body.appendChild(domElem);
  }

  close(): void {
    if (this.modalRef) {
      this.appRef.detachView(this.modalRef.hostView);
      this.modalRef.destroy();
      this.modalRef = null;
    }
  }
}

Creating a Reusable Modal Component

Design a generic modal component that can display various content and handle user interactions. This component will be dynamically created by the modal service.

Sample Modal Component

import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'app-custom-modal',
  template: `
    
  `,
  styles: [`
    .modal-backdrop {
      position: fixed;
      top: 0; left: 0;
      width: 100%; height: 100%;
      background: rgba(0,0,0,0.5);
      display: flex; justify-content: center; align-items: center;
    }
    .modal-content {
      background: #fff;
      padding: 20px;
      border-radius: 8px;
      min-width: 300px;
    }
  `]
})
export class CustomModalComponent {
  @Input() title: string = 'Modal Title';
  @Output() closeEvent = new EventEmitter();

  close() {
    this.closeEvent.emit();
  }

  onBackdropClick() {
    this.close();
  }
}

Using the Modal Service in Your Application

Inject the modal service into your component and trigger the open method to display the modal with desired content.

Example Usage

import { Component } from '@angular/core';
import { ModalService } from './services/modal.service';
import { CustomModalComponent } from './components/custom-modal.component';

@Component({
  selector: 'app-root',
  template: `
    
  `
})
export class AppComponent {
  constructor(private modalService: ModalService) {}

  openModal() {
    this.modalService.open(CustomModalComponent, { title: 'Reusable Dialog' });
  }
}

This setup creates a flexible modal system that can be reused throughout your Angular application, improving consistency and code maintainability.