DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Improving Backend Performance Part 1/3: Lazy Loading in Vaadin Apps
  • Leveraging Salesforce Using a Client Written In Angular
  • Bing Maps With Angular in a Spring Boot Application
  • Actuator Enhancements: Spring Framework 6.2 and Spring Boot 3.4

Trending

  • Pragmatica Aether: Let Java Be Java
  • Event-Driven Pipelines With Apache Pulsar and Go
  • Slopsquatting: Building a Scanner That Catches AI-Hallucinated Packages Before They Reach Production
  • Building a Spring AI Assistant With MCP Servers: A Step-by-Step Tutorial
  1. DZone
  2. Coding
  3. Frameworks
  4. Angular + Spring Boot + PrimeNG Datatable CRUD Example

Angular + Spring Boot + PrimeNG Datatable CRUD Example

In this tutorial, we will implement a PrimeNG datatable CRUD example. We will use Spring Boot for the backend to expose REST APIs.

By 
Nazia Shaikh user avatar
Nazia Shaikh
·
Dec. 24, 20 · Tutorial
Likes (2)
Comment
Save
Tweet
Share
15.0K Views

Join the DZone community and get the full member experience.

Join For Free

In previous tutorials, we implemented PrimeNG datatable inplace cell editing and PrimeNG datatable row editing. In this tutorial, we will implement a PrimeNG datatable CRUD example. We will use Spring Boot for the backend to expose REST APIs.

Technology Stack

We will be making use of:

  • Angular 9
  • PrimeNG
  • Spring Boot                     

Video tutorial:                               

Implementation

We will first implement the Spring Boot backend code to expose the REST APIs for the CRUD operations. In real-world application, Spring Boot will store the information using a database like MySQL. However, for this example, we will be storing the data in a temporary array.

The Spring Boot Maven project will be as follows.

pom.xml:

XML
 




x
29


 
1
<?xml version="1.0" encoding="UTF-8"?>
2
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4
    <modelVersion>4.0.0</modelVersion>  <groupId>com.codeusingjava</groupId>
5
    <artifactId>spring-boot-rest</artifactId>
6
    <version>1.0.0</version>
7
    <packaging>jar</packaging>  <name>spring-boot-rest</name>   <parent>
8
        <groupId>org.springframework.boot</groupId>
9
        <artifactId>spring-boot-starter-parent</artifactId>
10
        <version>2.3.0.RELEASE</version>
11
        <relativePath /> <!-- lookup parent from repository -->
12
    </parent>   <properties>
13
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
14
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
15
        <java.version>1.8</java.version>
16
    </properties>   <dependencies>
17
        <dependency>
18
            <groupId>org.springframework.boot</groupId>
19
            <artifactId>spring-boot-starter-web</artifactId>
20
        </dependency>
21
    </dependencies> <build>
22
        <plugins>
23
            <plugin>
24
                <groupId>org.springframework.boot</groupId>
25
                <artifactId>spring-boot-maven-plugin</artifactId>
26
            </plugin>
27
        </plugins>
28
    </build>
29
</project>


Create the Book model class as follows: 

Java
 




xxxxxxxxxx
1
15


 
1
package com.codeusingjava.model;public class Book { private String name;
2
    private String author;
3
    private int price;  public String getName() {
4
        return name;
5
    }   public void setName(String name) {
6
        this.name = name;
7
    }   public String getAuthor() {
8
        return author;
9
    }   public void setAuthor(String author) {
10
        this.author = author;
11
    }   public int getPrice() {
12
        return price;
13
    }   public void setPrice(int price) {
14
        this.price = price;
15
    }}


Create the controller class for exposing the REST APIs. We will be exposing 4 APIs.

Java
 




xxxxxxxxxx
1
54


 
1
package com.codeusingjava.controller;import java.util.ArrayList;
2
import java.util.List;import org.springframework.web.bind.annotation.CrossOrigin;
3
import org.springframework.web.bind.annotation.DeleteMapping;
4
import org.springframework.web.bind.annotation.GetMapping;
5
import org.springframework.web.bind.annotation.PathVariable;
6
import org.springframework.web.bind.annotation.PostMapping;
7
import org.springframework.web.bind.annotation.PutMapping;
8
import org.springframework.web.bind.annotation.RequestBody;
9
import org.springframework.web.bind.annotation.RequestMapping;
10
import org.springframework.web.bind.annotation.RestController;import com.codeusingjava.model.Book;@RestController
11
@RequestMapping("/api")
12
@CrossOrigin(origins = "http://localhost:4200")
13
public class BookController {   private List<Book> books = createList();    private static List<Book> createList() {
14
        List<Book> bookList = new ArrayList<>();        Book book1 = new Book();
15
        book1.setName("The Godfather");
16
        book1.setAuthor("Mario Puzo");
17
        book1.setPrice(10);     Book book2 = new Book();
18
        book2.setName("The Fellowship of the Ring");
19
        book2.setAuthor("J.R.R. Tolkien");
20
        book2.setPrice(15);     bookList.add(book1);
21
        bookList.add(book2);        return bookList;
22
    }   @GetMapping("/books")
23
    public List<Book> getAllBooks() {
24
        return books;
25
    }   @PostMapping("/books")
26
    public Book createBook(@RequestBody Book book) {
27
        System.out.println("Added Book - " + book.getName());
28
        books.add(book);
29
        return book;
30
    }   @PutMapping("/books/{name}")
31
    public Book updateBook(@PathVariable(value = "name") String name, @RequestBody Book bookDetails) {
32
        System.out.println("Updated Book - " + name);
33
        for (Book book : books) {
34
            if (book.getName().equals(name)) {
35
                books.remove(books.indexOf(book));
36
                books.add(bookDetails);
37
                break;
38
            }
39
        }
40
        return bookDetails;
41
    }   @DeleteMapping("/books/{name}")
42
    public Book deleteBook(@PathVariable(value = "name") String name) {
43
        System.out.println("Deleted Book - " + name);
44
        Book deletedBook = null;
45
        for (Book book : books) {
46
            if (book.getName().equals(name)) {
47
                books.remove(book);
48
                deletedBook = book;
49
                break;
50
            }
51
        }
52
        return deletedBook;
53
    }
54
}


Finally, create the Spring Boot bootstrap class for starting the application: 

Java
 




xxxxxxxxxx
1


 
1
package com.codeusingjava;import org.springframework.boot.SpringApplication;
2
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
3
public class BooksApplication { public static void main(String[] args) {
4
        SpringApplication.run(BooksApplication.class, args);
5
    }
6
}


Develop Angular Application

In a previous tutorial, we implemented a PrimeNG row editing example. We will be modifying this example perform CRUD operations using the PrimeNG datatable. 

Download the example.

Run the following command to build the project: npm install.

Start the project as follows: ng serve.

If we now go to localhost:4200/books, we can see the list of books populated in the PrimeNG datatable. This books data is currently hardcoded and being fetched from the books.json file in the assets folder.

We will first be implementing the GET functionality to get the books list from the Spring Boot backend to populate the datatable.

Modify the book.service.ts to make a GET call to the Spring Boot backend.

Java
 




x
12


 
1
import { Injectable } from '@angular/core';
2
import { HttpClient } from '@angular/common/http';export interface Book {
3
  name;
4
  price;
5
  author;
6
}@Injectable({
7
  providedIn: 'root'
8
})
9
export class BookService {  constructor(private http: HttpClient) {}  getBooks() {
10
    return this.http.get<Book[]>('http://localhost:8080/api/books');
11
    }
12
}


Above, the GET call returns an observable of Book array, which we need to subscribe to in the book component file.

Java
 




x
16


 
1
import { Component, OnInit } from '@angular/core';
2
import { BookService, Book } from '../service/book.service';@Component({
3
  selector: 'app-book-data',
4
  templateUrl: './book-data.component.html',
5
  styleUrls: ['./book-data.component.css']
6
})
7
export class BookDataComponent implements OnInit {  books: Book[];  constructor(private bookService: BookService) { }  ngOnInit() {
8
    this.bookService.getBooks().
9
    subscribe(books => this.books = books);
10
  }  onRowEditInit(book: Book) {
11
    console.log('Row edit initialized');
12
  }  onRowEditSave(book: Book) {
13
    console.log('Row edit saved');
14
  }  onRowEditCancel(book: Book, index: number) {
15
    console.log('Row edit cancelled');
16
  }}


Start the Spring Boot application and go to localhost:4200/books to get the list of books from the Spring Boot backend.

Next, we will be implementing the update Book functionality. We previously already added the edit icons in the data table and implemented the row edit functionality. Now, for the successful edit save, we will want to make a call the Spring Boot backend to update the book data. Also, if the edit is cancelled, we will want to revert the edit made, so we will be cloning the book list before the init is initialized to store the original book list and use it if the edit is cancelled.

Modify the book.service.ts to make a PUT call to the spring boot backend with the updated book data.

Java
 




x
14


 
1
import { Injectable } from '@angular/core';
2
import { HttpClient } from '@angular/common/http';export interface Book {
3
  name;
4
  price;
5
  author;
6
}@Injectable({
7
  providedIn: 'root'
8
})
9
export class BookService {  constructor(private http: HttpClient) {}  getBooks() {
10
    return this.http.get<Book[]>('http://localhost:8080/api/books');
11
    }  public updateBook(book) {
12
    return this.http.put<Book>("http://localhost:8080/api/books" + "/"+ book.name,book);
13
    }  
14
}


We will be making this update call when the edit is to be saved successfully, i.e. in the onRowEditSave method. Also, if the edit reverted, we will need the original data to be displayed again. So, we will do: 

Java
 




x
26


 
1
import { Component, OnInit } from '@angular/core';
2
import { BookService, Book } from '../service/book.service';@Component({
3
  selector: 'app-book-data',
4
  templateUrl: './book-data.component.html',
5
  styleUrls: ['./book-data.component.css']
6
})
7
export class BookDataComponent implements OnInit {  books: Book[];  constructor(private bookService: BookService) { }  ngOnInit() {
8
    this.bookService.getBooks().
9
    subscribe(books => this.books = books);
10
  }  clonedBooks: { [s: string]: Book; } = {};
11
  onRowEditInit(book: Book) {
12
    console.log('Row edit initialized');
13
    this.clonedBooks[book.name] = { ...book };
14
  }  onRowEditSave(book: Book) {
15
    console.log('Row edit saved');
16
    this.bookService.updateBook(book)
17
    .subscribe( data => {
18
      this.ngOnInit();
19
      alert("Book Updated successfully.");
20
    });
21
   
22
  }  onRowEditCancel(book: Book, index: number) {
23
    console.log('Row edit cancelled');
24
    this.books[index] = this.clonedBooks[book.name];
25
    delete this.clonedBooks[book.name];
26
  }}


Go to localhost:4200 and test the edit Book functionality.

Next, we will add the delete Book functionality.

Modify book.service.ts to make a DELETE call to the Spring Boot backend.

Java
 




x
18


 
1
import { Injectable } from '@angular/core';
2
import { HttpClient } from '@angular/common/http';export interface Book {
3
  name;
4
  price;
5
  author;
6
}@Injectable({
7
  providedIn: 'root'
8
})
9
export class BookService {  constructor(private http: HttpClient) {}  getBooks() {
10
    return this.http.get<Book[]>('http://localhost:8080/api/books');
11
    }  public updateBook(book) {
12
    return this.http.put<Book>("http://localhost:8080/api/books" + "/"+ book.name,book);
13
    } 
14
    
15
  public deleteBook(book) {
16
      return this.http.delete<Book>("http://localhost:8080/api/books" + "/"+ book.name);
17
    }
18
}


In the Book component file, add the delete function, which should be called when we want to delete a book from the table:

Java
 




x
35


 
1
import { Component, OnInit } from '@angular/core';
2
import { BookService, Book } from '../service/book.service';@Component({
3
  selector: 'app-book-data',
4
  templateUrl: './book-data.component.html',
5
  styleUrls: ['./book-data.component.css']
6
})
7
export class BookDataComponent implements OnInit {  books: Book[];  constructor(private bookService: BookService) { }  ngOnInit() {
8
    this.bookService.getBooks().
9
    subscribe(books => this.books = books);
10
  }  clonedBooks: { [s: string]: Book; } = {};
11
  onRowEditInit(book: Book) {
12
    console.log('Row edit initialized');
13
    this.clonedBooks[book.name] = { ...book };
14
  }  onRowEditSave(book: Book) {
15
    console.log('Row edit saved');
16
    this.bookService.updateBook(book)
17
    .subscribe( data => {
18
      this.ngOnInit();
19
      alert("Book Updated successfully.");
20
    });
21
   
22
  }  onRowEditCancel(book: Book, index: number) {
23
    console.log('Row edit cancelled');
24
    this.books[index] = this.clonedBooks[book.name];
25
    delete this.clonedBooks[book.name];
26
  }  deleteBook(book: Book) {
27
    console.log('Book Deleted');
28
     
29
    this.bookService.deleteBook(book)
30
      .subscribe( data => {
31
        this.ngOnInit();
32
        alert("Book Deleted successfully.");
33
      });
34
      
35
  }}


If we now go to localhost:4200/books, we will see the delete icon, which will delete the book upon clicking.

PrimeNG Datatable Spring Boot

We will now implement the add Book functionality.

Modify the book.service.ts to make a POST call to the Spring Boot backend.

Java
 




xxxxxxxxxx
1


 
1
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; 
2
export interface Book {  name;  price;  author; }
3
 @Injectable({  providedIn: 'root' }) export class BookService {   constructor(private http: HttpClient) {}   getBooks() {    return this.http.get<Book[]>('http://localhost:8080/api/books');    }   public updateBook(book) {    return this.http.put<Book>("http://localhost:8080/api/books" + "/"+ book.name,book);    }       public deleteBook(book) {      return this.http.delete<Book>("http://localhost:8080/api/books" + "/"+ book.name);    }      public createBook(book) {      return this.http.post<Book>("http://localhost:8080/api/books", book);    }
4
} 


We will be adding an Add Button in the HTML page for adding a new book. For this, we will need to get the book information from the user. We will get this information using an Angular form, which we will show in a dialog box.

We will first need to import the required modules in the app.module.ts file:

Java
 




xxxxxxxxxx
1
91


 
1
<p-table [value]="books" dataKey="name" editMode="row">
2
    <ng-template pTemplate="caption">
3
        List of Books
4
        <p-button class="btnAdd" label="ADD" (onClick)="onBookAdd()"></p-button>
5
      </ng-template>
6
    <ng-template pTemplate="header">
7
        <tr>
8
            <th>Name</th>
9
            <th>Author</th>
10
            <th>Price</th>
11
        </tr>
12
    </ng-template>
13
    <ng-template pTemplate="body" let-rowData let-editing="editing" let-ri="rowIndex">
14
        <tr [pEditableRow]="rowData">
15
            <td>                
16
                        {{rowData.name}}
17
                   
18
            </td>
19
            <td>
20
                <p-cellEditor>
21
                    <ng-template pTemplate="input">
22
                        <input pInputText type="text" [(ngModel)]="rowData.author">
23
                    </ng-template>
24
                    <ng-template pTemplate="output">
25
                        {{rowData.author}}
26
                    </ng-template>
27
                </p-cellEditor>
28
            </td>
29
            <td>
30
                <p-cellEditor>
31
                    <ng-template pTemplate="input">
32
                        <input pInputText type="text" [(ngModel)]="rowData.price">
33
                    </ng-template>
34
                    <ng-template pTemplate="output">
35
                        {{rowData.price}}
36
                    </ng-template>
37
                </p-cellEditor>
38
            </td>
39
            <td style="text-align:center">
40
                <p-button class="btnAdd" icon="pi pi-trash" class="ui-button-info" style="margin-right: .5em" (onClick)="deleteBook(rowData)"></p-button>                <button *ngIf="!editing" pButton type="button" pInitEditableRow icon="pi pi-pencil"
41
                    class="ui-button-info" (click)="onRowEditInit(rowData)"></button>
42
                <button *ngIf="editing" pButton type="button" pSaveEditableRow icon="pi pi-check"
43
                    class="ui-button-success" style="margin-right: .5em" (click)="onRowEditSave(rowData)"></button>
44
                <button *ngIf="editing" pButton type="button" pCancelEditableRow icon="pi pi-times"
45
                    class="ui-button-danger" (click)="onRowEditCancel(rowData, ri)"></button>
46
              
47
            </td>
48
        </tr>
49
    </ng-template>
50
   
51
</p-table><p-dialog header="Book Details" [(visible)]="displayDialog"
52
          [responsive]="true" showEffect="fade" [modal]="true">
53
    <form #BookForm="ngForm" novalidate>
54
        <div class="ui-grid ui-grid-responsive ui-fluid" *ngIf="bookForDialog">
55
            <div class="ui-g ui-g-12 ui-g-nopad">
56
                <div class="ui-g-12 ui-md-3 ui-label">
57
                    <label for="fname">Book Name</label>
58
                </div>
59
                <div class="ui-g-12 ui-md-9">
60
                    <input pInputText id="fname" name="fname" required
61
                           [(ngModel)]="bookForDialog.name"/>
62
                </div>
63
            </div>
64
            <div class="ui-g ui-g-12 ui-g-nopad">
65
                <div class="ui-g-12 ui-md-3 ui-label">
66
                    <label for="lname">Author</label>
67
                </div>
68
                <div class="ui-g-12 ui-md-9">
69
                    <input pInputText id="lname" name="lname" required
70
                           [(ngModel)]="bookForDialog.author"/>
71
                </div>
72
            </div>
73
            <div class="ui-g ui-g-12 ui-g-nopad">
74
                <div class="ui-g-12 ui-md-3 ui-label">
75
                    <label for="prof">Price</label>
76
                </div>
77
                <div class="ui-g-12 ui-md-9">
78
                    <input pInputText id="prof" name="prof" required
79
                           [(ngModel)]="bookForDialog.price"/>
80
                </div>
81
            </div>
82
            
83
        </div>
84
    </form>
85
    <p-footer>
86
        <div class="ui-dialog-buttonpane ui-helper-clearfix">
87
            <button type="submit" pButton icon="fa-check" (click)="saveBook()"
88
                    label="Save" [disabled]="!BookForm.form.valid"></button>
89
        </div>
90
    </p-footer>
91
    </p-dialog>


We are done with the changes. Now, when the user clicks on the Add button, a form will be displayed. When the user clicks on the Save button, the book will be added.

Spring Framework Spring Boot Book AngularJS

Opinions expressed by DZone contributors are their own.

Related

  • Improving Backend Performance Part 1/3: Lazy Loading in Vaadin Apps
  • Leveraging Salesforce Using a Client Written In Angular
  • Bing Maps With Angular in a Spring Boot Application
  • Actuator Enhancements: Spring Framework 6.2 and Spring Boot 3.4

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook