Angular – Material Dialog Box

Table of Contents
Overview
The example is a basic example of an Angular Material Dialog Box. This post will walk through how to create the application, install and configure Material, and build a Material Dialog Box. The application will have a table that will open a record in the dialog box. In the dialog box a text field will hold the text that is shown from the parent component in a Material Snack Bar.
Source Files For This Article
Source CodeCreate a New Angular Application
First, create a new application
ng new ng-demp-app
Once the application has been created CD into the new application folder.
cd ng-demo-app
Install Material with Angular Schematics – ng add. This will take care of installing Material and it dependencies and basic configuration. You will still need to take care of the imports which is discussed in the next section.
ng add @angular/material
So your package.json file should have the following dependencies.

Generate Components and Service
Create the following components – Assuming all components go under a folder labeled components (Optional)
ng generate component components/dialog-demo-data-table --module=app
ng generate component components/parent-layout --module=app
ng generate service services/data
Material Module
Because there are a lot of imports to using Material, most examples I have seen use a separate module to handle these imports. Below is a step by step on creating the module and adding it to the root module “app.module.ts”.
Create a new module labeled material.module.ts. Here is a post on how to do this.
Make sure you have the following imports in your apps.module.ts file.
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpClientJsonpModule, HttpClientModule } from '@angular/common/http';
import { MaterialModule } from './material.module';
@NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserModule,
AppRoutingModule,
ReactiveFormsModule,
BrowserAnimationsModule,
HttpClientModule,
HttpClientJsonpModule,
BrowserAnimationsModule,
FormsModule,
MaterialModule
],
providers: [],
entryComponents: [],
bootstrap: [AppComponent]
})
export class AppModule { }
You can learn more about this from material.angular.io.
Create or choose an existing component to add the bottom sheet to.
Add the imports needed to the Root app.module.ts or Feature Module.
import { MatBottomSheet } from '@angular/material/bottom-sheet';
....
@NgModule({
declarations: [],
imports: [
MatBottomSheet,
....
JSON Data
List of chemicals from the Angular Material example. Create a file under the “/assets” folder.

Note: It’s important to put the data files under assets for accessibility.
[
{
"position": 1,
"name": "Hydrogen",
"weight": 1.0079,
"symbol": "H"
},
{
"position": 2,
"name": "Helium",
"weight": 4.0026,
"symbol": "He"
},
{
"position": 3,
"name": "Lithium",
"weight": 6.941,
"symbol": "Li"
},
{
"position": 4,
"name": "Beryllium",
"weight": 9.0122,
"symbol": "Be"
},
{
"position": 5,
"name": "Boron",
"weight": 10.811,
"symbol": "B"
},
{
"position": 6,
"name": "Carbon",
"weight": 12.0107,
"symbol": "C"
},
{
"position": 7,
"name": "Nitrogen",
"weight": 14.0067,
"symbol": "N"
},
{
"position": 8,
"name": "Oxygen",
"weight": 15.9994,
"symbol": "O"
},
{
"position": 9,
"name": "Fluorine",
"weight": 18.9984,
"symbol": "F"
},
{
"position": 10,
"name": "Neon",
"weight": 20.1797,
"symbol": "Ne"
}
]
Add an Interface
Create a new interface file by using the Angular CLI command – This is optional. You can just add the export interface in the service and component(s).
ng g interface interfaces/Chemicals
Add the following to the Interface.
export interface IChemicals {
position?: any;
name?: number;
weight?: boolean;
symbol: string;
}
HTTP Service
The service will simulate an API Call by getting the JSON file data.json from the assets folder.,
Add the following code for the service
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { IChemicals } from '../interfaces/IChemicals';
@Injectable({
providedIn: 'root'
})
export class DataService {
constructor(private http: HttpClient) { }
getAll(): Observable<IChemicals[]> {
return this.http.get<IChemicals[]>("../../assets/data.json");
}
}
Data Table Component
Create a Material Table. This example was taken from Angular Materials website on Tables here. For this example the data is stored in a JSON file under the assets folder and an action button created to emit an ID to the parent that opens the dialog box. This is basically the data table example from Angular Material Table
TypeScript
export interface PeriodicElement {
name: string;
position: number;
weight: number;
symbol: string;
}
@Component({
selector: 'app-dialog-demo-data-table',
templateUrl: './dialog-demo-data-table.component.html',
styleUrls: ['./dialog-demo-data-table.component.scss']
})
export class DialogDemoDataTableComponent implements OnInit, AfterViewInit, OnDestroy {
@Input() title: string;
@Output() openRecord = new EventEmitter<number>();
@ViewChild(MatSort) sort: MatSort;
private subs = new Subscription();
constructor(private dataSVC: DataService) {}
public dataSource: any;
displayedColumns: string[] = ['action', 'position', 'name', 'weight', 'symbol'];
ngOnInit(): void {
}
ngAfterViewInit(): void {
this.subs.add(
this.dataSVC.getAll()
.subscribe((data) => {
this.dataSource = new MatTableDataSource(data);
this.dataSource.sort = this.sort;
},
(err: HttpErrorResponse) => {
console.log(err);
}));
}
ngOnDestroy(): void {
if (this.subs) {
this.subs.unsubscribe();
}
}
onOpenDialog(id: number): void {
console.log(id);
this.openRecord.emit(id);
}
}
HTML
Below is the HTML for the Material Data Table. There are a few variations of how to implement these so this may be outdated by the time you see this.
<table mat-table [dataSource]="dataSource" matSort class="mat-elevation-z8">
<ng-container matColumnDef="action">
<th mat-header-cell *matHeaderCellDef > </th>
<td mat-cell *matCellDef="let element">
<button mat-flat-button color="default" id="deleteBtn" (click)="onOpenDialog(element.position)">
Open
</button>
</td>
</ng-container>
<!-- Position Column -->
<ng-container matColumnDef="position">
<th mat-header-cell *matHeaderCellDef mat-sort-header> No. </th>
<td mat-cell *matCellDef="let element"> {{element.position}} </td>
</ng-container>
<!-- Name Column -->
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Name </th>
<td mat-cell *matCellDef="let element"> {{element.name}} </td>
</ng-container>
<!-- Weight Column -->
<ng-container matColumnDef="weight">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Weight </th>
<td mat-cell *matCellDef="let element"> {{element.weight}} </td>
</ng-container>
<!-- Symbol Column -->
<ng-container matColumnDef="symbol">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Symbol </th>
<td mat-cell *matCellDef="let element"> {{element.symbol}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
Parent Component
In the root folder of your application create two files labeled app-dialog.html and app-dialog.css (Optional but add it for the purposes of this example). The Parent Component will have a Data Table that emits the ID to the parent onOpenDialog(id) method. The dialog shows the record and accepts text to be shared with the parent.

The Dialog Entry Component should be in the same folder as full component.
The following imports are for the Dialog Box. Snack Bar (optional), data service, RxJs Subscription and Http Error Reponse, and IChemicals interface.
import { HttpErrorResponse } from '@angular/common/http';
import { Component, OnInit, Inject, OnDestroy } from '@angular/core';
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Subscription } from 'rxjs/internal/Subscription';
import { DataService } from 'src/app/services/data.service';
import { IChemicals } from '../../../interfaces/IChemicals';
On Init
Use OnOnit() to get the data from the JSON file and OnDestroy() to close all RxJs Subs()
export class ParentLayoutComponent implements OnInit, OnDestroy {
ngOnInit(): void {
this.subs.add(
this.dataSVC.getAll()
.subscribe((response) => {
this.data = response;
},
(err: HttpErrorResponse) => {
console.log(err);
}));
}
Open Dialog Function
To open the dialog use the open() method as shown in the openDialog() method.
openDialog(id: number): void {
this.record = this.data.filter(c => c.position === id)[0];
const dialogRef = this.dialog.open(MatDialogDemo, {
width: '450px',
height: '375px',
data: {chemData: this.record}
});
dialogRef.afterClosed().subscribe(result => {
if (result) {
this.openSnackBar(`Message From Dialog: ${result}`, 'Close')
}
});
}
Entry Component – Dialog Box
At the bottom of the Parent Component add the Entry Component with the @Component decorator. So essentially, there can be multiple components in a file. All of the examples of Entry Components I have seen are declared with the parent component.
@Component({
selector: 'mat-dialog',
templateUrl: './mat-dialog.component.html',
styleUrls: ['./mat-dialog.component.scss'],
})
export class MatDialogDemo {
constructor(
public dialogRef: MatDialogRef<MatDialogDemo>,
@Inject(MAT_DIALOG_DATA) public data: DialogData) {}
onNoClick(): void {
this.dialogRef.close();
}
}
All together – Complete Typescript File
Notice both @Component declarations are ordered as main component at the top and the entry component on the bottom.
import { HttpErrorResponse } from '@angular/common/http';
import { Component, OnInit, Inject, OnDestroy } from '@angular/core';
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Subscription } from 'rxjs/internal/Subscription';
import { DataService } from 'src/app/services/data.service';
import { IChemicals } from '../../../interfaces/IChemicals';
export class ParentLayoutComponent implements OnInit, OnDestroy {
private data: any[] = [];
private subs = new Subscription();
public record: IChemicals;
constructor(private fb: FormBuilder,
private dataSVC: DataService,
private dialog: MatDialog) {}
ngOnInit(): void {
this.subs.add(
this.dataSVC.getAll()
.subscribe((response) => {
this.data = response;
},
(err: HttpErrorResponse) => {
console.log(err);
}));
}
ngOnDestroy(): void {
if (this.subs) {
this.subs.unsubscribe();
}
}
openSnackBar(message: string, action: string) {
this.snackBar.open(message, action, {
duration: 5000,
verticalPosition: 'bottom',
panelClass: ['mat-toolbar', 'mat-primary']
});
}
openDialog(id: number): void {
this.record = this.data.filter(c => c.position === id)[0];
const dialogRef = this.dialog.open(MatDialogDemo, {
width: '450px',
height: '375px',
data: {chemData: this.record}
});
dialogRef.afterClosed().subscribe(result => {
if (result) {
this.openSnackBar(`Message From Dialog: ${result}`, 'Close')
}
});
}
}
@Component({
selector: 'mat-dialog',
templateUrl: './mat-dialog.component.html',
styleUrls: ['./mat-dialog.component.scss'],
})
export class MatDialogDemo {
constructor(
public dialogRef: MatDialogRef<MatDialogDemo>,
@Inject(MAT_DIALOG_DATA) public data: DialogData) {}
onNoClick(): void {
this.dialogRef.close();
}
}
HTML – Parent Layout Component
Parent-layout.component.html just has the dialog-demo-data-table component.
<app-dialog-demo-data-table (openRecord)="openDialog($event)"></app-dialog-demo-data-table>
Dialog – Entry Component
Create two files labeled mat-dialog-component.html and mat-dialog-component.scss in the same folder as the parent component.

Dialog Styling
Styling for the Dialog Box material components (Optional).
.card-body {
position: relative;
background-color: #fffafa;
height: 300px;
width: 100%;
overflow: hidden;
}
.circle {
width: 200px;
height: 450px;
border-radius: 70%;
background: #e7e5e5;
position: absolute;
right: -100px;
top: -50px;
}
.center-image {
display: block;
margin-left: 45px;
margin-right: auto;
width: 75%;
}
img {
border-radius: 50%;
}
Dialog HTML
Using material list items to display the data looked up from the chemical ID.
<div class="card-body">
<h1 mat-dialog-title>Chemical</h1>
<div mat-dialog-content>
<mat-list>
<mat-list-item>No: {{data.chemData.position}}</mat-list-item>
<mat-list-item>Symbol: {{data.chemData.symbol}} </mat-list-item>
<mat-list-item>Name: {{data.chemData.name}} </mat-list-item>
<mat-list-item>Weight: {{data.chemData.weight}} </mat-list-item>
</mat-list>
<mat-form-field class="example-full-width" appearance="outline">
<mat-label>Leave a comment</mat-label>
<textarea matInput placeholder="" [(ngModel)]="data.comment"></textarea>
</mat-form-field>
</div>
<div mat-dialog-actions>
<button mat-raised-button color="primary" [mat-dialog-close]="onCloseDialog" [mat-dialog-close]="data.comment" cdkFocusInitial >Submit</button>
<button mat-raised-button color="warn" mat-dialog-close>Close</button>
</div>
</div>
Add Component to the Root Module
Update app,module.ts by adding the component to the declarations and entryComponents.
import { ParentComponentComponent, MatDialogDemo } from './components/mat-dialog-demo/parent-component/parent-component.component';
Add the Dialog Component under Declarations and as an Entry Component.
@NgModule({
declarations: [
AppComponent,
ParentBottomSheetLayoutComponent,
ParentComponent,
MatDialogDemo,
DialogDemoDataTableComponent
],
imports: [
...
],
providers: [],
entryComponents: [
MatDialogDemo
],
bootstrap: [AppComponent]
})
export class AppModule { }
You must be logged in to post a comment.