Creating an Angular Material Table with Editable Fields using Dialogs

In this tutorial, we will learn how to create an Angular Material table with editable fields by using dialogs. We will also perform CRUD (Create, Read, Update, Delete) operations on the data.

We will be using Angular Reactive Forms to handle form validation.

Prerequisites

  • Basic knowledge of Angular
  • Angular CLI installed

Step 1: Create a new Angular project

Open a terminal and run the following command to create a new Angular project:

ng new my-app

 

Step 2: Install Material and CDK

Run the following command to install Angular Material and the Component Development Kit (CDK) in your project:

ng add @angular/material

 

Step 3: Create a data model

Create a new file named data.ts in the src/app directory and define a data model as follows:

export class Data {
    id: number;
    name: string;
    email: string;
    phone: string;
}

 

Step 4: Create a table component

Run the following command to generate a new table component:

ng generate component table

This will create a new folder named table in the src/app directory, containing the following files:

  • table.component.ts
  • table.component.html
  • table.component.css

Open the table.component.ts file and update with the following code in it:

import { Component, OnInit } from '@angular/core';
import { MatTableDataSource } from '@angular/material/table';
import { MatDialog } from '@angular/material/dialog';
import { DialogComponent } from '../dialog/dialog.component';
import { Data } from '../data';

@Component({
  selector: 'app-table',
  templateUrl: './table.component.html',
  styleUrls: ['./table.component.css'],
})
export class TableComponent implements OnInit {
  dataSource = new MatTableDataSource<Data>();
  displayedColumns: string[] = ['id', 'name', 'email', 'phone', 'action'];

  constructor(public dialog: MatDialog) {}

  ngOnInit(): void {
    this.dataSource.data = [
      {
        id: 1,
        name: 'John Doe',
        email: '[email protected]',
        phone: '123-456-7890',
      },
      {
        id: 2,
        name: 'Jane Smith',
        email: '[email protected]',
        phone: '098-765-4321',
      },
    ];
  }

  addData(): void {
    const dialogRef = this.dialog.open(DialogComponent, {
      width: '250px',
      data: { type: 'add', data: new Data() },
    });

    dialogRef.afterClosed().subscribe((result) => {
      if (result) {
        this.dataSource.data.push(result);
        this.dataSource._updateChangeSubscription();
      }
    });
  }

  updateData(data: Data): void {
    const dialogRef = this.dialog.open(DialogComponent, {
      width: '250px',
      data: { type: 'update', data: data },
    });

    dialogRef.afterClosed().subscribe((result) => {
      if (result) {
        const index = this.dataSource.data.findIndex((x) => x.id === result.id);
        this.dataSource.data[index] = result;
        this.dataSource._updateChangeSubscription();
      }
    });
  }

  viewData(data: Data): void {
    this.dialog.open(DialogComponent, {
      width: '250px',
      data: { type: 'view', data: data },
    });
  }

  deleteData(id: number): void {
    this.dataSource.data = this.dataSource.data.filter((x) => x.id !== id);
    this.dataSource._updateChangeSubscription();
  }
}

And here is the complete code for the table.component.html file:

<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">

    <ng-container matColumnDef="id">
        <th mat-header-cell *matHeaderCellDef> ID </th>
        <td mat-cell *matCellDef="let element"> {{element.id}} </td>
    </ng-container>
    <ng-container matColumnDef="name">
        <th mat-header-cell *matHeaderCellDef> Name </th>
        <td mat-cell *matCellDef="let element"> {{element.name}} </td>
    </ng-container>

    <ng-container matColumnDef="email">
        <th mat-header-cell *matHeaderCellDef> Email </th>
        <td mat-cell *matCellDef="let element"> {{element.email}} </td>
    </ng-container>

    <ng-container matColumnDef="phone">
        <th mat-header-cell *matHeaderCellDef> Phone </th>
        <td mat-cell *matCellDef="let element"> {{element.phone}} </td>
    </ng-container>

    <ng-container matColumnDef="action">
        <th mat-header-cell *matHeaderCellDef class="text-center"> Action </th>
        <td mat-cell *matCellDef="let element" class="text-center">
            <button mat-icon-button (click)="viewData(element)">
                <mat-icon>remove_red_eye</mat-icon>
            </button>
            <button mat-icon-button (click)="updateData(element)">
                <mat-icon>edit</mat-icon>
            </button>
            <button mat-icon-button (click)="deleteData(element.id)">
                <mat-icon>delete</mat-icon>
            </button>
        </td>
    </ng-container>

    <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
    <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>

<button mat-raised-button color="primary" (click)="addData()">Add</button>

 

Step 5: Create a dialog component

Run the following command to generate a new dialog component:

ng generate component dialog

This will create a new folder named dialog in the src/app directory, containing the following files:

  • dialog.component.ts
  • dialog.component.html
  • dialog.component.css

Open the dialog.component.ts file and update with below code:

import { Component, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { Data } from '../data';

@Component({
  selector: 'app-dialog',
  templateUrl: './dialog.component.html',
  styleUrls: ['./dialog.component.css'],
})
export class DialogComponent {
  form: FormGroup;
  type: string;
  data: Data;

  constructor(
    public dialogRef: MatDialogRef<DialogComponent>,
    @Inject(MAT_DIALOG_DATA) public inputData: any,
    private formBuilder: FormBuilder
  ) {
    this.type = inputData.type;
    this.data = inputData.data;

    this.form = this.formBuilder.group({
      name: [this.data.name, Validators.required],
      email: [this.data.email, [Validators.required, Validators.email]],
      phone: [
        this.data.phone,
        [Validators.required, Validators.pattern('^[0-9]*$')],
      ],
    });
  }

  onNoClick(): void {
    this.dialogRef.close();
  }

  onSaveClick(): void {
    if (this.form.valid) {
      this.dialogRef.close(this.form.value);
    }
  }
}

In this component, we are using MatDialogRef and MAT_DIALOG_DATA from Angular Material to create a dialog and receive data from the parent component. We are also using FormBuilder, FormGroup and Validators from Angular Forms to create the form and handle validation. We are injecting the data that is passed from the parent component to the dialog component via the inputData variable, and then using that data to create the form and set the values of the form controls.

The onNoClick() function is used to close the dialog when the user clicks the “Cancel” button, and the onSaveClick() function is used to save the form data and close the dialog when the user clicks the “Save” button.

Here is the complete code for the dialog.component.html file:

<h1 mat-dialog-title>{{ type == 'add' ? 'Add' : type == 'update' ? 'Update' : 'View' }} Data</h1>
<form [formGroup]="form" (ngSubmit)="onSaveClick()" *ngIf="type != 'view'">
    <div mat-dialog-content>
        <mat-form-field>
            <input matInput formControlName="name" placeholder="Name">
            <mat-error *ngIf="form.get('name')?.hasError('required')">
                Name is <strong>required</strong>
            </mat-error>
        </mat-form-field>
        <mat-form-field>
            <input matInput formControlName="email" placeholder="Email">
            <mat-error *ngIf="form.get('email')?.hasError('required')">
                Email is <strong>required</strong>
            </mat-error>
            <mat-error *ngIf="form.get('email')?.hasError('email')">
                Email is <strong>not valid</strong>
            </mat-error>
        </mat-form-field>
        <mat-form-field>
            <input matInput formControlName="phone" placeholder="Phone">
            <mat-error *ngIf="form.get('phone')?.hasError('required')">
                Phone is <strong>required</strong>
            </mat-error>
        </mat-form-field>
    </div>
    <div mat-dialog-actions>
        <button mat-button (click)="onNoClick()">Cancel</button>
        <button mat-raised-button color="primary" [disabled]="form.invalid" type="submit">{{ type == 'add' ? 'Add' :
            'Update' }}</button>
    </div>
</form>
<div mat-dialog-content *ngIf="type == 'view'">
    <p>Name: {{data.name}}</p>
    <p>Email: {{data.email}}</p>
    <p>Phone: {{data.phone}}</p>
</div>
<div mat-dialog-actions *ngIf="type == 'view'">
    <button mat-button (click)="onNoClick()">Close</button>
</div>

 

You can now run the application by using the following command:

ng serve --open

 

It should display the table with data and a button to add data. You can add, update and delete the data.

 

Conclusion

we have learned how to create an Angular Material table with editable fields using dialogs, and how to perform CRUD operations on the data. We also learned how to handle form validation using Angular Reactive Forms.

This approach can be used for creating dynamic forms in Angular, and can also be applied to other types of data such as products, customers, or orders.

Leave a Comment

Your email address will not be published. Required fields are marked *