Of all the Angular Material components, the MatDialog just may be the most complex. At the same time, it is probably also the most versatile of the bunch. Part of the reason is that it’s not a component so much as a service that can be utilized to open modal dialogs with Material Design styling and animations. In this tutorial, we’ll replace the standard JavaScript confirm dialog that we implemented in the Preventing Data Loss In Angular Applications using a CanDeactivate Route Guard tutorial with a MatDialog:
JavaScript Confirm Dialog
Angular Confirm Dialog
Adding MatDialog to the Material Module File
Recall that we placed all of our Angular Material imports in the srcappsharedmodules
import {MatDialogModule} from '@angular/material/dialog'; const materialModules = [ //... MatToolbarModule, MatDialogModule ];
Creating the ConfirmDialog Component
Part of what makes MatDialog so versatile is that its open() method accepts a component to show in the body of the dialog. You might be tempted to create the component as a child to the one that will call it, but it might be wise to think twice before doing so as we may like to reuse the same dialog in other places within out application in the future. For that reason, I would recommend generating it within the app directory:
ng g c confirm-dialog
In the confirm-dialog.component.ts file, we will modify the constructor to accept a reference to the dialog as well as the data that we will pass to it:
import { Component, Inject, ViewEncapsulation } from '@angular/core'; import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; @Component({ selector: 'app-confirm-dialog', templateUrl: './confirm-dialog.component.html', styleUrls: ['./confirm-dialog.component. css'], // this will allow us to override the mat-dialog-container CSS class encapsulation: ViewEncapsulation.None }) export class ConfirmDialogComponent { constructor( public dialogRef: MatDialogRef< ConfirmDialogComponent>, @Inject(MAT_DIALOG_DATA) public data: any) { } }
Next, we’ll add the contents of the dialog to the confirm-dialog.component.html file:
<div class="dialog-header accent-background"> <span class="dialog-header-title">{{data.dialogTitle}}</span> </div> <div class="dialog-content"> <p>{{data.dialogMessageLine1}} <br/> {{data.dialogMessageLine2}}</ p> </div> <div class="dialog-footer"> <button class="standard-button dialog-button" mat-raised-button [mat-dialog-close]="false" cdkFocusInitial>{{data. noButtonText}}</button> <button mat-raised-button color="primary" [mat-dialog-close]="true">{{ data.yesButtonText}}</button> </div>
Invoking the MatDialog Service
Back in the survey.component.ts file, we’re ready to update the canExit() method to present our custom dialog instead of the native JavaScript confirm dialog. There are three things we need to do to make that happen:
- Add a constructor that accepts a MatDialog reference.
- Add the openUnsavedChangesDialog() method. It’s responsible for showing the dialog.
- Invoke the openUnsavedChangesDialog() method from canExit().
Here is the updated source code for the survey.component.ts file that shows the relevant changes:
// imports import { MatDialog } from "@angular/material/dialog"; import { ConfirmDialogComponent } from "../confirm-dialog/confirm-dialog.component"; // SatisfactionRatings enum @Component({ selector: "app-survey", templateUrl: "./survey.component.html", styleUrls: ["./survey.component.css"] }) export class SurveyComponent implements IDeactivateComponent { // declarations constructor(public dialog: MatDialog) { } //methods... public canExit(): boolean | Observable<boolean> { return this.ngFormRef.dirty ? this.openUnsavedChangesDialog( ) : true; }; private openUnsavedChangesDialog(): Observable<boolean> { const dialogRef = this.dialog.open( ConfirmDialogComponent, { width: '26.5rem', data: { dialogTitle: 'Unsaved Changes', dialogMessageLine1: 'You have unsaved changes.', dialogMessageLine2: 'Are you sure you want to leave the page?', yesButtonText: 'Leave this Page', noButtonText: 'Stay on this Page' } }); return dialogRef.afterClosed(); } }
The openUnsavedChangesDialog() Method Explained
There’s a lot going on in this little method, so let’s unpack it.
The dialog reference that we injected via the constructor provides numerous methods, properties, and event hooks for working with it, the most important of which being the open() method. It accepts the component to display as well as a MatDialogConfig object. That’s where we set the dialog’s appearance and pass along the data object that populates the dialog component.
Organizations must go beyond a piecemeal approach to networking and security. A broad, integrated, and automated platform that secures all edges addresses challenges now and in the future.
The afterClosed() event hook receives an observable that is notified when the dialog is finished closing. We can do whatever processing we need to do after the dialog is closed. In our case, we don’t need to do anything but pass along the value returned by the dialog. That gets set by the two buttons in the footer via the bound [mat-dialog-close] attribute:
<div class="dialog-footer"> <button class="standard-button dialog-button" mat-raised-button [mat-dialog-close]="false" cdkFocusInitial>{{data.noButtonText}}</button> <button mat-raised-button color="primary" [mat-dialog-close]="true">{{ data.yesButtonText}}</button> </div>
We then need to add the Observable<boolean> return type to canExit() to accommodate the afterClosed() return value.
Here’s the end result of today’s updates to the Preventing Data Loss In Angular Applications using a CanDeactivate Route Guard demo. To test it, navigate to the Survey page, interact with the form so as to update the underlying model, and then click the Home link:
Conclusion
In this tutorial, we learned how to use the MatDialog, the most complex, and yet most versatile Angular Material component. To do that, we replaced the standard JavaScript confirm dialog that we implemented in the Preventing Data Loss In Angular Applications using a CanDeactivate Route Guard demo with a MatDialog.