It is a common requirement for any Angular application to prompt the user for a confirmation dialog to verify the action being taken. 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 to achieve this with Material components.

Traditional Approach

Traditionally, web-based applications utilise the built-in JavaScript features to show alerts & dialogs 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
Default JavaScript Confirmation Dialog

Angular 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.

You can get the full source code from below or run the live demo.

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 blog post and learn how to properly import it.

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 may wish to 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 ConfirmDialog
Code 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 view 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.

Umut Esen

Umut is a certified Microsoft certified developer and has an MSc in Computer Science. He is currently working as a senior software developer in Edinburgh, UK. He is the primary author and the founder of onthecode.

This Post Has 10 Comments

  1. gar519

    I really needed this. works PERFECTLY!

    THANKS

  2. Michal

    Thanks a lot !

  3. 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.

  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. 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));
    }

  6. 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.

Leave a Reply