CRUD Operations using primeng datatable and Material Design

In this post , we are going to use PrimeNG which provides rich set of open source native Angular UI components and Angular Material Design components. Using these two UI libraries , we will built a application to perform CRUD operations.

Code for this example can be downloaded from below links –

Front End Code

Backend Code

Step 1: Install primeng

https://www.primefaces.org/primeng/#/setup

This package provides us lot of inbuilt UI components to design the User interface as per our requirements. We have created CustomPrimengModule which will have all the primeng components dependencies defined in it. We are using these primeng modules by importing CustomPrimengModule in main app module i.e. aap.module.ts file.

Step 2: Install Angular material

https://material.angular.io/guide/getting-started

Angular Material is a UI component library forAngular JS developers by google. Angular Material components help in constructing attractive, consistent, and functional web pages and web applications while adhering to modern web design principles like browser portability, device independence, and graceful degradation.

I have created CustomMaterialModule which will have all the material components dependencies defined in it. We are using these primeng modules by importing CustomMaterialModule in main app module i.e. aap.module.ts file. Also we need to make one entry for importing styles for material design theme – @import “../node_modules/@angular/material/prebuilt-themes/deeppurple-amber.css”;

Step 3: Define Reactive forms module

I have also defined ReactiveFormsModule and AppRoutingModule which are required for creating forms and routing.

Step 4: Install App notifications Module

https://www.npmjs.com/package/angular-notifier

We are also using NotifierModule to show the notifications in application. For this , we need to import NotifierModule in app.module.ts. Also we need to make one entry for importing styles for notifications – @import “~angular-notifier/styles”;

Step 5 : Create the components/Services/Models required for application

  • Components:

ng g c components/todo –main=app

ng g c components/todo-add –main=app

ng g c components/todo-edit –main=app

  • Services:

ng g s services/todo

  • Model:

Create a typescript file Todo.ts

Step 6: Write the component to perform CRUD operations

src\app\components\todo\todo.component.html

This is the view part which displays primeng data table providing features for sorting, pagination,inline row editing, Delete, Add row etc.

 <div id="list" *ngIf="displayTodoList">
  <p-table [columns]="cols" [value]="todos" sortField="status" sortMode="single" [paginator]="true" [rows]="10"
    [globalFilterFields]="['status','desc']" dataKey="name" editMode="row">
    <ng-template pTemplate="caption">
      List of Todo Tasks
      <p-button class="btnAdd" label="ADD" (onClick)="addTodo()"></p-button>
    </ng-template>
    <ng-template pTemplate="header" let-columns>
      <tr>
        <th *ngFor="let col of columns" [pSortableColumn]="col.field">
          {{col.header}}
          <p-sortIcon [field]="col.field"></p-sortIcon>
        </th>
      </tr>
    </ng-template>

    <ng-template pTemplate="body" let-todo let-editing="editing" let-ri="rowIndex">

      <tr [pEditableRow]="todo">
        <td>
          <p-cellEditor>
            <ng-template pTemplate="input">
              <input pInputText type="text" [(ngModel)]="todo.name">
            </ng-template>
            <ng-template pTemplate="output">
              {{todo.name}}
            </ng-template>
          </p-cellEditor>
        </td>
        <td>
          <p-cellEditor>
            <ng-template pTemplate="input">
              <input pInputText type="text" [(ngModel)]="todo.desc">
            </ng-template>
            <ng-template pTemplate="output">
              {{todo.desc}}
            </ng-template>
          </p-cellEditor>
        </td>
        <td>
          <p-cellEditor>
            <ng-template pTemplate="input">
              <p-dropdown [options]="statusValues" [(ngModel)]="todo.status" [style]="{'width':'100%'}"></p-dropdown>
            </ng-template>
            <ng-template pTemplate="output">
              {{todo.status}}
            </ng-template>
          </p-cellEditor>
        </td>
        <td style="text-align:center">
          <p-button class="btnAdd" icon="pi pi-trash" class="ui-button-info" (onClick)="deleteTodo(todo.id)"></p-button>
             
          <button *ngIf="!editing" pButton type="button" pInitEditableRow icon="pi pi-pencil" class="ui-button-info"
            (click)="onRowEditInit(todo)"></button>
          <button *ngIf="editing" pButton type="button" pSaveEditableRow icon="pi pi-check" class="ui-button-success"
            style="margin-right: .5em" (click)="onRowEditSave(todo)"></button>
          <button *ngIf="editing" pButton type="button" pCancelEditableRow icon="pi pi-times" class="ui-button-danger"
            (click)="onRowEditCancel(todo, ri)"></button>

        </td>
      </tr>
    </ng-template>

    <ng-template pTemplate="summary">
      There are {{todos?.length}} todo tasks
    </ng-template>

  </p-table>
</div>
<todo-add *ngIf="displayTodoAdd" (valueChange)='hideTodoAdd($event)'></todo-add>
<!-- <router-outlet></router-outlet> -->

src\app\components\todo\todo.component.ts

This is a typescript file which has implementation for CRUD operations on primeng datatable. It uses TodoService to perform CRUD operations on data using REST webservice –

import { Component, OnInit } from '@angular/core';
import { TodoService } from 'src/app/services/todo.service';
import { Router } from '@angular/router';
import { map, catchError, tap, retry } from 'rxjs/operators';
import { NotifierService } from 'angular-notifier';
import { Todo } from 'src/app/domain/Todo';
import { SelectItem } from 'primeng/primeng';

@Component({
  selector: 'todo-list',
  templateUrl: './todo.component.html',
  styleUrls: ['./todo.component.css']
})
export class TodoComponent implements OnInit {
  cols: any[];
  todos: Todo[];
  displayedColumns: string[] = ['Task Name', 'Task Description', 'Status', 'action'];

  displayTodoList = true;
  displayTodoAdd = false;
  displayTodoEdit = false;

  constructor(private todoService: TodoService, private router: Router, private notifier: NotifierService) {
  }

  ngOnInit() {
    this.displayTodoList = true;
    this.displayTodoAdd = false;
    this.displayTodoEdit = false;

    console.log("From Init = " + this.displayTodoList + "  " + this.displayTodoAdd);
    this.getTodoList().subscribe((data) => { this.todos = data });

    this.cols = [
      { field: 'name', header: 'Task Name' },
      { field: 'desc', header: 'Task Description' },
      { field: 'status', header: 'Status' },
      { field: 'action', header: 'Action' }
    ];

    this.statusValues = [
      { label: 'Done', value: 'Done' },
      { label: 'Pending', value: 'Pending' },
      { label: 'Cancelled', value: 'Cancelled' }
    ];
  }

  // To Get List Of Todos
  getTodoList() {
    return this.todoService.getTodoList();
  }

  // To Get Todo
  getTodo(todoId) {
    return this.todoService.getTodo(todoId);
  }

  // To Edit Todo
  editTodo(todoId) {
    this.displayTodoList = false;
    this.displayTodoEdit = true;
    this.router.navigate([`/todo-edit/${todoId}`]);
  }

  //Delete Todo
  deleteTodo(todoId) {
    console.log("Delete id" + todoId);
    this.todoService.deleteTodo(todoId).subscribe((data) => {
      console.log("success");
      this.notifier.notify("success", "Task deleted successfully!!");
    });
    this.ngOnInit();
    this.router.navigate([`/`]);
  }

  //add Todo
  addTodo() {
    console.log("Before = " + this.displayTodoList + "  " + this.displayTodoAdd);
    this.displayTodoList = false;
    this.displayTodoAdd = true;
    console.log("After =" + this.displayTodoList + "  " + this.displayTodoAdd);
  }

  hideTodoAdd(event) {
    console.log("Event emiited " + event);
    this.displayTodoList = true;
    this.displayTodoAdd = event;
  }

  clonedTodos: { [s: string]: Todo; } = {};
  statusValues: SelectItem[];

  onRowEditInit(todo: Todo) {
    this.clonedTodos[todo.id] = { ...todo };
  }

  onRowEditSave(todo: Todo) {
    this.todoService.updateTodo(todo.id, todo).subscribe((data) => {
      console.log("success");
      this.notifier.notify("success", "Task updated successfully!!");
    });
  }

  onRowEditCancel(todo: Todo, index: number) {
    this.todos[index] = this.clonedTodos[todo.id];
    delete this.clonedTodos[todo.id];
  }
}

Step 7: Service Layer – We have injected TodoService through constructor and used it to perform CRUD operations .This service has GET,POST,PUT,DELETE calls to REST endpoints .

src\app\services\todo.service.ts

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

@Injectable({
  providedIn: 'root'
})
export class TodoService {

  endpoint = 'http://localhost:8081/api';

  constructor(private http: HttpClient) {
  }

  // Http Options
  httpOptions = {
    headers: new HttpHeaders({
      'Content-Type': 'application/json'
    })
  }

  // To Get List Of Todos
  getTodoList(): Observable<any> {
    return this.http.get(this.endpoint + '/todos')
      .pipe(
        retry(1),
        catchError(this.handleError)
      );
  }

  // HttpClient API get() method => Fetch Todo
  getTodo(id): Observable<Todo> {
    return this.http.get<Todo>(this.endpoint + '/todos/' + id)
      .pipe(
        retry(1),
        catchError(this.handleError)
      )
  }

  // HttpClient API post() method => Create Todo
  createTodo(todo): Observable<Todo> {
    return this.http.post<Todo>(this.endpoint + '/todo', JSON.stringify(todo), this.httpOptions)
      .pipe(
        retry(1),
        catchError(this.handleError)
      )
  }

  // HttpClient API put() method => Update Todo
  updateTodo(id, todo): Observable<Todo> {
    return this.http.put<Todo>(this.endpoint + '/todo' , JSON.stringify(todo), this.httpOptions)
      .pipe(
        retry(1),
        catchError(this.handleError)
      )
  }

  // HttpClient API delete() method => Delete Todo
  deleteTodo(id):Observable<Todo[]> {
    console.log("Delete id "+ id);
    return this.http.delete<Todo[]>(this.endpoint+ '/todo/' + id);

  }

  // Error handling 
  handleError(error) {
    let errorMessage = '';
    if (error.error instanceof ErrorEvent) {
      // Get client-side error
      errorMessage = error.error.message;
    } else {
      // Get server-side error
      errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;
    }
    window.alert(errorMessage);
    return throwError(errorMessage);
  }
}

Step 8: Domain Layer – It contains model to hold the todo tasks data.

export interface Todo {
  
    id?;
	name?;
	desc?;
	status?;
}

Step 9: Now we are ready to run the project –

ng serve

Step 10: Screenshots –

Main page with data Grid

Add Task screen

Inline Task Editing screen

Deleting task –

2 thoughts on “CRUD Operations using primeng datatable and Material Design”

  1. great example! thank you.
    but in Add Task screen when data submit, then push BACK button , Main page with data Grid did not list new data
    maybe in todo.component.ts fix
    hideTodoAdd(event) {
    console.log(“Event emiited ” + event);
    this.displayTodoList = true;
    this.displayTodoAdd = event;
    this.getTodoList().subscribe((data) => { this.todos = data }); //<—
    }

    Reply

Leave a Comment

Bitnami