In this tutorial I will show you how you can create a reusable dialog module with Angular Material for your Angular projects!

Prerequisites

Angular 7 or later

Angular Material 7 or later

Creating the module

First of all, you must create a folder that will contain all of your files. I chose to create it here: app/shared/confirm-dialog

The final structure of the module will be the following:

Module structure

Let’s create the first file.

Start from the confirmation component template and name it confirm-dialog.component.html

As mentioned before, we will use the Material Design Component Dialog .

You can see more information about Dialog’s API here.

This component has several directives that help us to make it easier to structure our dialog content. Below some information about them from Material Design Documentation:

mat-dialog-title : Dialog title, applied to a heading element

<mat-dialog-content> : Primary scrollable content of the dialog.

<mat-dialog-actions> : Container for action buttons at the bottom of the dialog.

mat-dialog-close : Added to a <button> , makes the button close the dialog with an optional result from the bound value.

<div class="header">

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

</div>

<div mat-dialog-content>

<p class="dialog-message">{{ data.message }}</p>

</div>

<div mat-dialog-actions>

<button class="btn btn-cancel" style="margin-right:10px;" (click)="cancel()">{{ data.cancelText }}</button>

<button class="btn" (click)="confirm()">{{ data.confirmText }}</button>

</div>

The first file is ready! Let’s write the functionality of this component. Create the second file named: confirm-dialog.component.ts

Start by adding the parts of the code that are common in every component: imports, component decorator, class definition.

Please pay attention to the templateUrl to add the correct file path!

import { Component } from '@angular/core'; @Component({

selector: 'app-confirm-dialog',

templateUrl: './confirm-dialog.component.html',

styles: []

}) export class ConfirmDialogComponent {

constructor(){}

}

Then import and inject MAT_DIALOG_DATA token, that can be used to access the data that are passed in to a dialog, in the constructor. Also define the format of that data.

For the confirmation dialog, we will need a title , a message and two buttons: cancel and confirm . Use these as parameters to make the component reusable.

import { MAT_DIALOG_DATA } from "@angular/material"; ... constructor(@Inject(MAT_DIALOG_DATA) public data: {

cancelText: string,

confirmText: string,

message: string,

title: string

}){}

You will also need to import and inject MatDialogRef and a reference to the dialog opened via the MatDialog service.

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

import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material"; ... constructor(@Inject(MAT_DIALOG_DATA) public data: {

cancelText: string,

confirmText: string,

message: string,

title: string

}, private mdDialogRef: MatDialogRef<ConfirmDialogComponent>){}

What’s left is to define some methods for the component’s behavior. What is going to happen when confirm or cancel or close button is clicked or ESC key is pressed?

... public cancel() {

this.close(false);

} public close(value) {

this.mdDialogRef.close(value);

} public confirm() {

this.close(true);

} @HostListener("keydown.esc")

public onEsc() {

this.close(false);

}

In each case we have to return the value, to inform the caller if the user confirmed the message. In close , cancel and ESC cases send false and in confirm case send true .

Your file is almost ready! The only thing that remains is to set some styles. Nothing fancy, just lowercase/uppercase some text and make the cancel button red.

... styles: [`

.header, .dialog-message {

text-transform: lowercase;

}

.header::first-letter, .dialog-message::first-letter {

text-transform: uppercase;

}

.btn-cancel {

background-color: red;

color: #fff;

}

`] ...

Putting everything together, that’s the final class.

You can see the final class here:

Final confirm-dialog.component.ts

Confirm Service

We will continue the implementation with the creation of an injectable service. This will help you use the functionality everywhere!

Let’s start by adding the parts of the code that are common in every service: imports, injectable decorator, class definition.

import { Injectable } from '@angular/core'; @Injectable() export class ConfirmDialogService {

constructor() { }

}

In our service we want to open a dialog and get its response. Create two methods that do exactly that.

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

import { Observable } from 'rxjs'; @Injectable() export class ConfirmDialogService {

constructor() { } public open(options) {} public confirmed(): Observable<any> {} }

The open method will open a dialog with the given options , so there is no need to return anything, but the confirmed method had to return an observable to the caller, because it has to wait for a response. The next step is to implement those two methods.

The most important things here are:

Define the correct component. This will be used as the dialog Inject the Material Design’s MatDialog service. This will be used to open modal dialogs with Material Design styling and animations.

... import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';

import { ConfirmDialogComponent } from './confirm-dialog.component'; ... export class ConfirmDialogService {

constructor(private dialog: MatDialog) { } dialogRef: MatDialogRef<ConfirmDialogComponent>; public open(options) {

this.dialogRef = this.dialog.open(ConfirmDialogComponent, {

data: {

title: options.title,

message: options.message,

cancelText: options.cancelText,

confirmText: options.confirmText

}

});

} public confirmed(): Observable<any> {} }

You can check more about the Material Design’s built-in dialog service here.

One done, one remains! As we already have a dialog opened, we can handle its response, so let’s implement confirmed method!

...

import { map, take } from 'rxjs/operators';

... public confirmed(): Observable<any> {



return this.dialogRef.afterClosed().pipe(take(1), map(res => {

return res;

}

));

} ...

We will return an observable that is notified when the dialog closing is finished.

The dialog service is ready!

This is the complete class. Save it as confirm-dialog.service.ts in the same directory.

You can see the final class here:

Final confirm-dialog.service.ts

The Module file

You can see the class here:

Final confirm-dialog.module.ts

Don’t forget to add the component to entryComponents!

Use it in your Component

Follow the steps below to add a dialog to your component:

Import the service

import { ConfirmDialogService } from 'app/shared/confirm-dialog/confirm-dialog.service'; Inject the service

constructor(private dialogService: ConfirmDialogService, …) Prepare the dialog options

const options = {

title: 'CONFIRM.DOWNLOAD.JOB.TITLE',

message: 'CONFIRM.DOWNLOAD.JOB.MESSAGE',

cancelText: 'CONFIRM.DOWNLOAD.JOB.CANCELTEXT',

confirmText: 'CONFIRM.DOWNLOAD.JOB.CONFIRMTEXT'

};

4. Open the dialog

this.dialogService.open(options);

5. Subscribe to the confirmed observable

this.dialogService.confirmed().subscribe(confirmed => {

if (confirmed) {

//do something if confirmed is true

}

});

Example case

We want to display a confirmation dialog when the save button is clicked.

This dialog will have the following data:

Title: Leave page?

Leave page? Message: By leaving this page you will permanently lose your form changes.

By leaving this page you will permanently lose your form changes. Cancel text: CANCEL

CANCEL Confirm text: YES, LEAVE PAGE

Template of the button in the component (html file)

<button (click)="handleClick()">Save</button>

Then implement the handleClick method in the component class.

import { ConfirmDialogService } from 'app/shared/confirm-dialog/confirm-dialog.service'; ... constructor(private dialogService: ConfirmDialogService) {} ... handleClick() {

const options = {

title: 'Leave page?',

message: 'By leaving this page you will permanently lose your form changes.',

cancelText: 'CANCEL',

confirmText: 'YES, LEAVE PAGE'

};



this.dialogService.open(options);



this.dialogService.confirmed().subscribe(confirmed => {

if (confirmed) {

this.saveData();

}

});

} saveData() {

...

}

The result will be the following:

Example of confirmation dialog

Conclusion

Building reusable modules, like the dialog in this article, can be beneficial for the development process. Reduce code duplication, lesser time spent in testing, UI consistency and a cleaner way to code, are some of the most important benefits.

This module can become more powerful if other dialog formats (for example informative modals) are added.

I hope you found this Medium guide useful!