It is a common requirement for any Angular application to prompt the user for a confirmation dialog. For instance, you may want to show a message when the user wants to delete a record or send an email. In this post, we will look at how we can implement a reusable component in Angular Material components.

Basic confirm dialog in JavaScript

Traditionally, web applications make use of the built-in JavaScript API to show confirm dialog as shown below.

However, the vanilla JavaScript dialogs are synchronous – meaning everything on the screen will freeze until the user responds, they can’t be customised visually and don’t support images.

// Display a confirmation prompt
var result = confirm("Are you sure?");Code language: JavaScript (javascript)
Default JavaScript confirm dialog
Basic JavaScript Confirmation Dialog

Angular Material confirm dialog

To overcome the limitations of the traditional approach, we can easily build a flexible confirmation dialog in Angular with a bit of help from the Material components.

Here is what we are going to build:

Angular Material Confirm Dialog Modal Popup
Custom Confirm Dialog in Angular Material

We will use the Dialog component of Angular Material library to display our confirm dialogs.

The dialog takes the focus away from the current window, and forces the browser to read the message. The following are the main features of this custom component:

  • Asynchronous Behaviour: We want to show the dialog and subscribe to its result. This is very important as we don’t know when the user is going to respond. Asynchrony allows us to properly react to the response of the user.
  • Flexibility: We want to customise the title & message of the confirm dialog so that we can display any message from anywhere in the application.

Check out the code on GitHub or view live demo here.

Import Angular Material components

First of all, you need to import Angular Material to your application. If you need help with this, check out my guide.

Once you have access to Material components, import ButtonModule and MatDialogModule.

import { NgModule } from '@angular/core';
import { MatButtonModule, MatDialogModule } from '@angular/material';

@NgModule({
  imports: [
    MatButtonModule, MatDialogModule
  ],
  exports: [
    MatButtonModule, MatDialogModule
  ]
})
export class CustomMaterialModule {}Code language: TypeScript (typescript)

I am importing Material UI components to a feature module called CustomMaterialModule.

You can import them directly to the app.module.

Generate a component

Next, generate a component for the confirmation dialog using the following command:

ng generate component ConfirmDialogCode language: Bash (bash)

This will automagically create your component and link it with your root app.module.

Open the app.module and add the newly-created component to the entryComponents array. This is so that the confirm dialog can be dynamically loaded into the view – read more about entry components.

import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { CustomMaterialModule } from './custom-material/custom-material.module';
import { ConfirmDialogComponent } from './confirm-dialog/confirm-dialog.component';

@NgModule({
  declarations: [
    AppComponent,
    ConfirmDialogComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    CustomMaterialModule
  ],
  providers: [],
  entryComponents: [ConfirmDialogComponent],
  bootstrap: [AppComponent]
})
export class AppModule {}Code language: TypeScript (typescript)

Implement dialog features

Basically, we just want to show a heading, message and buttons to dismiss the dialog.

Here is what the template looks like:

<h1 mat-dialog-title>
  {{title}}
</h1>

<div mat-dialog-content>
  <p>{{message}}</p>
</div>

<div mat-dialog-actions>
  <button mat-button (click)="onDismiss()">No</button>
  <button mat-raised-button color="primary" (click)="onConfirm()">Yes</button>
</div>Code language: HTML, XML (xml)

As you can see, this is a standard Material dialog implementation.

The component class to handle UI interaction is as follows:

import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
import { Component, OnInit, Inject } from '@angular/core';

@Component({
  selector: 'app-confirm-dialog',
  templateUrl: './confirm-dialog.component.html',
  styleUrls: ['./confirm-dialog.component.css']
})
export class ConfirmDialogComponent {
  title: string;
  message: string;

  constructor(public dialogRef: MatDialogRef&lt;ConfirmDialogComponent>,
    @Inject(MAT_DIALOG_DATA) public data: ConfirmDialogModel) {
    // Update view with given values
    this.title = data.title;
    this.message = data.message;
  }
 
  onConfirm(): void {
    // Close the dialog, return true
    this.dialogRef.close(true);
  }

  onDismiss(): void {
    // Close the dialog, return false
    this.dialogRef.close(false);
  }
}

/**
 * Class to represent confirm dialog model.
 *
 * It has been kept here to keep it as part of shared component.
 */
export class ConfirmDialogModel {

  constructor(public title: string, public message: string) {
  }
}Code language: TypeScript (typescript)

The component just handles the events by closing the Material dialog with the selected result from the view.

ConfirmDialogModel is used to pass data into the dialog.

Display dialog on button click

Finally, we can show our custom confirmation dialog using the code below.

Since we use MatDialog component internally, we take in the MatDialog dependency in the constructor and show the confirm dialog on button click.

Subscribing to the afterClosed() event of the dialog means that we can get the result and update the view through data-binding.

Add a button to the view to trigger the dialog, and a label to show the response:

<button mat-raised-button color="primary" (click)="confirmDialog()">Confirm</button>
<br>
Response is: {{result}}Code language: HTML, XML (xml)

and the component for the functionality:

import { Component } from '@angular/core';
import { ConfirmDialogModel, ConfirmDialogComponent } from './confirm-dialog/confirm-dialog.component';
import { MatDialog } from '@angular/material';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {
  result: string = '';

  constructor(public dialog: MatDialog) {}

  confirmDialog(): void {
    const message = `Are you sure you want to do this?`;

    const dialogData = new ConfirmDialogModel("Confirm Action", message);

    const dialogRef = this.dialog.open(ConfirmDialogComponent, {
      maxWidth: "400px",
      data: dialogData
    });

    dialogRef.afterClosed().subscribe(dialogResult => {
      this.result = dialogResult;
    });
  }
}Code language: TypeScript (typescript)

Summary

In conclusion, we looked at how we can create a reusable component in Angular to display confirmation dialogs. Similarly, this approach can be used to implement other types of dialogs such as alerts, date/time input dialogs & more.

Check out the code on GitHub.

Umut Esen

Software Engineer specialising in full-stack web application development.

Leave a Reply

This Post Has 11 Comments

  1. John

    Very good article.

  2. Penny Thomas

    Seeing an error in stackblitz on the example code in this article:

    Error in /~/src/app/confirm-dialog/confirm-dialog.component.ts (44:156)
    Cannot access ‘ConfirmDialogModel’ before initialization

    This error occurs randomly, have been unable to determine the cause.

    1. Umut Esen

      Thank you for your message, I have tried to replicate the issue but stackblitz seems to be working ok. Please try downloading the source code.

  3. Travis

    Instead of injecting MatDialog into your component you could create a service that provides the confirmDialog method to create and open the modal and a behavior subject result value that can be subscribed to from your componet.

    export class ConfirmDialogService {
    result$ = new BehavoirSubject(false);

    constructor(public dialog: MatDialog){}

    confirmDialog(title: string, message:string) {

    dialogRef.afterClosed().subscribe(res => this.result$.next(res));
    }

  4. Swathi

    Nice article. Keep it up. Just a question – For different events, I don’t want to create different components rather want to have a single component which can have all the dialogs content. How can I achieve this?

    1. Umut Esen

      Hi there! The `ConfirmDialogModel` class demonstrated in the article can transfer pretty much any kind of content, including complex objects, to the dialog. You can declare new properties in that class and render the contents in the shared dialog html. Depending on the complexity of your events, you may want to consider rendering components in the dialog via routing. Good luck!

  5. bablu

    how did you said that this is reusable.

    1. Umut Esen

      Hi bablu, the component is re-usable in the sense that it can be placed in a shared module and used in any other module. You could also package it up as an npm package to include in different projects.

  6. Michal

    Thanks a lot !

  7. gar519

    I really needed this. works PERFECTLY!

    THANKS