Delete Multiple Table Rows From Server Using Angular

Introduction

In this tutorial, I will show you how to delete multiple rows from HTML table as well as from database server at one go. You might have seen tutorial how to display data from server to client side and how to select multiple rows on HTML table.

I will put checkbox against each row on the table so that user will be able to select a particular row for deletion. I will also put a checkbox on the table header and selecting this checkbox all rows on the table will be selected for deletion.

If user selects manually all checkboxes for rows on the table then the checkbox in table header will be checked automatically to show that all checkboxes on the table body are checked. If any of the checkboxes on the table body gets unchecked the header checkbox will be unchecked automatically.

Prerequisites

Angular 9/13, Node 16.12.0, Npm 8.1.0, CSS, HTML, REST API Endpoints – /products and /delete/products

Project Setup

How to Create Angular Project

Execute the command ng new angular-delete-multiple-rows-from-table in CLI tool to create a new angular project.

Model Class

I will map JSON object from the server into client side object. The class file product.ts is created under src/app folder.

Therefore I am creating a class called Product which has below attributes:

export class Product {
  id?: number;
  name: string;
  code: string;
  price: number;
  checked?: boolean;
}

The class is straight forward and notice ?, which indicates optional value for id and checked fields.

Generally ? is not required on id field when you are fetching data from server but may be required when you want to save data if you are generating id value on server side.

The checked field indicates whether a particular checkbox is checked/selected or not.

In Angular 13, I am using interface instead of class for model.

export interface Product {
  id?: number;
  name: string;
  code: string;
  price: number;
  checked?: boolean;
}

Service Class

Service class is required to process your business logic. The class file product.service.ts is created under src/app folder.

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable, of } from 'rxjs';
//import { catchError, map, tap } from 'rxjs/operators';
import { Product } from './product';

const httpOptions = {
  headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
@Injectable({ providedIn: 'root' })
export class ProductService {
  private productUrl = 'http://localhost:8080';  // URL to REST API
  
  constructor(private http: HttpClient) { }
  
  /** GET products from the server */
  getProducts(): Observable<Product[]> {
    return this.http.get<Product[]>(this.productUrl + '/products');
  }
  
  /** DELETE: delete the product from the server */
  deleteProducts(ids: number[]) {
	  if (confirm("Are you sure to delete?")) {
		const data = {'ids' : ids};
		const url = `${this.productUrl}/delete/products`;
		const options = {
			headers: new HttpHeaders({
				'Accept': 'application/json',
				'Content-Type': 'application/json'
			}),
			responseType: 'text' as 'json'
		};
		
		const resp = this.http.post<any>(url, data, options);//.map(resp => {return resp;}).catch(err => {console.log(err);});
		
		//console.log('resp: ' + resp);
		
		return resp;
	  }
	  
	  return of({});
  }
}

Notice I have used @Injectable({ providedIn: 'root' }) to make the service class singleton.

A singleton service is a service for which only one instance exists in an app.

There are two ways to make a service a singleton in Angular:

  • Set the providedIn property of the @Injectable() to “root”.
  • Include the service in the AppModule or in a module that is only imported by the AppModule (explained later).

I have defined two functions – one for fetching data from server and another one for deleting data from the server. For delete also I am using HTTP method POST because I want to pass multiple product ids to delete products from the MySQL database.

Change Page Title Globally

I will replace the title in the file src/index.html with the below title:

<title>Delete Multiple Table Rows from Server using Angular</title>

If you want to change title for specific title then you have set in the corresponding *.component.ts file.

Load Required Module Globally

I need to import required modules, such as, HttpClientModule and FormsModule into the file src/app/app.module.ts file.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule }    from '@angular/common/http';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
	FormsModule,
    BrowserModule,
	HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

I explained about making service class singleton and I could have included the ProductService class into the above file inside provides: [], such as, providers: [ProductService].

Load/Delete Data – Component

In order to display data on HTML file I need to load data into corresponding component TypeScript file. So here I have only app component, so I am going to use app.component.ts file under src/app directory.

I have also created to check individual or all checkbox are checked/selected or not.

I have defined function to delete the selected products from the HTML table.

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

import { Product } from './product';
import { ProductService } from './product.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
	//msg: string; // Angular 9
        //clss: string; // Angular 9
        msg: string = ''; // Angular 13
        clss: string = ''; // Angular 13
	products: Product[] = [];
	
	constructor(private productService: ProductService) { }
	
	ngOnInit() {
		this.getProducts();
	}
	
	getProducts(): void {
		this.productService.getProducts().subscribe(products => this.products = products);
	}
	
	//checkAllCheckBox(ev) { // Angular 9
        checkAllCheckBox(ev: any) { // Angular 13
		this.products.forEach(x => x.checked = ev.target.checked)
	}

	isAllCheckBoxChecked() {
		return this.products.every(p => p.checked);
	}
	
	deleteProducts(): void {
		const selectedProducts = this.products.filter(product => product.checked).map(p => p.id);
		//console.log (selectedProducts);
		
		if(selectedProducts && selectedProducts.length > 0) {
			//this.productService.deleteProducts(selectedProducts) // Angular 9
                        this.productService.deleteProducts(selectedProducts as number[]) // Angular 13
				.subscribe(res => {
					this.clss = 'grn';
					this.msg = 'Products successfully deleted';
					}, err => {
                        this.clss = 'rd';
						this.msg = 'Something went wrong during deleting products';
                    }
                );
		} else {
			this.clss = 'rd';
			this.msg = 'You must select at least one product';
		}
	}
}

Display Data on HTML Table

Now I will display data on HTML table and provide the checkbox to check/select rows on HTML table for deletion.

I have put a button above the table to delete the selected products. If you do not select any row from the table then you will see error message.

I have put two <div/>s to display messages – error and success. Error message is shown in red color while success message is shown in green color.

<h1>Delete Multiple Table Rows from Server using Angular</h1>

<div *ngIf="products && products.length > 0; else elseBlock">
	<div *ngIf="msg" [ngClass]="clss">{{msg}}</div>
	<button type="button" (click)="deleteProducts()">Delete Selected Product(s)</button>
	<table class="datatable">
		<thead>
			<tr>
				<th><input type="checkbox" [checked]="isAllCheckBoxChecked()" (change)="checkAllCheckBox($event)"></th>
				<th>ID</th>
				<th>Code</th>
				<th>Name</th>
				<th>Price</th>
			</tr>
		</thead>
		<tbody>
			<tr *ngFor="let p of products; let i=index; let odd = odd" [ngClass]="odd ? 'odd_col' : 'even_col'">
				<td><input type="checkbox" value="{{p.id}}" [(ngModel)]="products[i].checked"></td>
				<td>{{ p.id }}</td>
				<td>{{ p.name }}</td>
				<td>{{ p.code }}</td>
				<td>{{ p.price }}</td>
			</tr>
		</tbody>
	</table>
</div>
<ng-template #elseBlock><div style="color: red;">No record found</div></ng-template>

Apply Style

Apply basic style on table and two different colors for alternate rows.

I have also applied the color to the error and success message <div/>s.

table.datatable {
	width:100%;
	border: none;
	background:#fff;
}
table.datatable td.table_foot {
	border: none;
	background: #fff;
	text-align: center;
}
table.datatable tr.odd_col {
	background: none;
}
table.datatable tr.even_col {
	background: #ddd;
}
table.datatable td {
	font-size:10pt;
	padding:5px 10px;
	text-align: left;
}
table.datatable th {
	text-align: left;
	font-size: 8pt;
	padding: 10px 10px 7px;   
	text-transform: uppercase;
	color: #fff;
	background-color: black;
	font-family: sans-serif;
}

.rd {
	color: red;
}

.grn {
	color: green;
}

Testing the Application

Finally execute the command ng serve --open on project’s root directory to run the application and the application opens up automatically in the browser at http://localhost:4200.

The below youtube video shows how it works.

Source Code

Download

Leave a Reply

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