Spring Boot + Angular + PostgreSQL CRUD Example

In this tutorial, we will learn how to build a CRUD full-stack application using Angular as frontend and Spring boot as backend. We will use the PostgreSQL database.

Prerequisites 

There is a separate tutorial for building CRUD REST APIs using spring boot and PostgreSQL database - Spring Boot PostgreSQL CRUD Example tutorial. 

In this tutorial, we will build CRUD Angular application that consumes REST APIs exposed in 
Spring Boot PostgreSQL CRUD Example tutorial.

So before building an Angular application, make sure that your Spring boot application is up and running and all the five REST APIs working:

Architecture


What we will build?

Basically, we will create two projects:
  1. springboot-backend: Develop and expose REST APIs. For building REST APIs, refer to Spring Boot PostgreSQL CRUD Example tutorial.
  2. angular-frontend: Consume REST APIs that are exposed by Spring boot REST APIs.
Below are the screenshots shows the UI of our Employee Management System App.

Employee List Page

Add Employee Page

Update Employee Page

Building Angular Frontend Application

Let's develop a step-by-step CRUD (Create, Read, Update, Delete) web application using Angular 12 which consumes CRUD rest APIs.
I assume that you have installed Node.js. Now, we need to check the Node.js and NPM versions. Open the terminal or Node command line then type these commands.
G:\angular\Angular 12>node -v
v12.18.2

G:\angular\Angular 12>npm -v
6.14.5

1. Install the latest version of Angular CLI

To install or update Angular CLI, type this command in the Terminal or Node Command-Line.
npm install -g @angular/cli
Now, let's check the latest version of Angular CLI:
G:\angular\Angular 12>ng --version

     _                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/


Angular CLI: 12.1.3
Node: 12.18.2
Package Manager: npm 6.14.5
OS: win32 x64

Angular:
...

Package                      Version
------------------------------------------------------
@angular-devkit/architect    0.1201.3 (cli-only)
@angular-devkit/core         12.1.3 (cli-only)
@angular-devkit/schematics   12.1.3 (cli-only)
@schematics/angular          12.1.3 (cli-only)

2. Create Angular App using Angular CLI

The Angular CLI is a command-line interface tool that you use to initialize, develop, scaffold, and maintain Angular applications.
If you are new to Angular CLI then check out official documentation at https://cli.angular.io.
Let's use the below command to generate an Angular Client application. We name this project as "angular-frontend".
ng new angular-frontend

3. Identify Components, Services, and Modules

Let's list out what are componentsservices, and modules we are going to create in this application. We will use Angular CLI to generate components, services because Angular CLI follows best practices and saves much time.

Components

  • create-employee
  • update-employee
  • employee-list
  • employee-details

Services

  • employee.service.ts - Service for HTTP Client methods

Modules

We use below in-built modules provided by Angular:
  • FormsModule
  • HttpClientModule
  • AppRoutingModule

Employee Class (Typescript class)

  • employee.ts: class Employee (id, firstName, lastName, emailId)
In the next step, we will generate these components, classes, and services using Angular CLI.

4. Create Angular Components and Service Classes using Angular CLI

Let's auto-generate the service and components using Angular CLI. Change your project directory to angular-frontend\src\app and run the following commands:
– ng g c create-employee
– ng g c update-employee
– ng g c employee-details
– ng g c employee-list
- ng g s employee

5. Integrate JQuery and Bootstrap with Angular

Use NPM to download Bootstrap & JQueryBootstrap and jQuery will be installed into the node_modules folder.
npm install bootstrap jquery --save
Configure installed Bootstrap & JQuery in an angular.json file:
...
 
"styles": [
  "src/styles.css",
  "node_modules/bootstrap/dist/css/bootstrap.min.css"
],
"scripts": [
  "node_modules/jquery/dist/jquery.min.js",
  "node_modules/bootstrap/dist/js/bootstrap.min.js"
]
 
...
If bootstrap won't work then try to import bootstrap CSS in style.css like:
/* You can add global styles to this file, and also import other style files */

@import '~bootstrap/dist/css/bootstrap.min.css';

.footer {
    position: absolute;
    bottom: 0;
    width:100%;
    height: 70px;
    background-color: blue;
    text-align: center;
    color: white;
}

6. Create an Employee Model (TypeScript)

Path - src/app/employee.ts
Before defining the EmployeeListComponent, let’s define an Employee class for working with employees. create a new file employee.ts inside src/app folder and add the following code to it -
export class Employee {
    id: number;
    firstName: string;
    lastName: string;
    emailId: string;
}

7. Create Employee Service - REST Client

Path - src/app/employee.service.ts
The EmployeeService will be used to get the data from the backend by calling spring boot APIs. Update the employee.service.ts file inside src/app directory with the following code to it -
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http'
import { Observable } from 'rxjs';
import { Employee } from './employee';

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

  private baseURL = "http://localhost:8080/api/v1/employees";

  constructor(private httpClient: HttpClient) { }
  
  getEmployeesList(): Observable<Employee[]>{
    return this.httpClient.get<Employee[]>(`${this.baseURL}`);
  }

  createEmployee(employee: Employee): Observable<Object>{
    return this.httpClient.post(`${this.baseURL}`, employee);
  }

  getEmployeeById(id: number): Observable<Employee>{
    return this.httpClient.get<Employee>(`${this.baseURL}/${id}`);
  }

  updateEmployee(id: number, employee: Employee): Observable<Object>{
    return this.httpClient.put(`${this.baseURL}/${id}`, employee);
  }

  deleteEmployee(id: number): Observable<Object>{
    return this.httpClient.delete(`${this.baseURL}/${id}`);
  }
}
In the next step, we will start creating Angular components.

8. Creating Employee List Component and Template

Path - src/app/employee-list/employee-list.component.ts

Let's create the EmployeeListComponent component which will be used to display a list of employees, create a new employee, and delete an employee.
Update/remove the content of employee-list.component.ts inside src/app directory and add the following code to it -
import { Component, OnInit } from '@angular/core';
import { Employee } from '../employee'
import { EmployeeService } from '../employee.service'
import { Router } from '@angular/router';
@Component({
  selector: 'app-employee-list',
  templateUrl: './employee-list.component.html',
  styleUrls: ['./employee-list.component.css']
})
export class EmployeeListComponent implements OnInit {

  employees: Employee[];

  constructor(private employeeService: EmployeeService,
    private router: Router) { }

  ngOnInit(): void {
    this.getEmployees();
  }

  private getEmployees(){
    this.employeeService.getEmployeesList().subscribe(data => {
      this.employees = data;
    });
  }

  employeeDetails(id: number){
    this.router.navigate(['employee-details', id]);
  }

  updateEmployee(id: number){
    this.router.navigate(['update-employee', id]);
  }

  deleteEmployee(id: number){
    this.employeeService.deleteEmployee(id).subscribe( data => {
      console.log(data);
      this.getEmployees();
    })
  }
}

Path - src/app/employee-list/employee-list.component.html

Add employee-list.component.html file with the following code to it -
<div class = "row">
    <h2> Employee List</h2>
</div>

<table class = "table table-striped table-bordered">
    <thead>
        <tr>
            <th> First Name</th>
            <th> Last Name </th>
            <th> Email Id</th>
            <th> Actions </th>
        </tr>
    </thead>
    <tbody>
        <tr *ngFor = "let employee of employees" >
            <td> {{ employee.firstName }} </td>
            <td> {{ employee.lastName }} </td>
            <td> {{ employee.emailId }} </td>
            <td>
                <button (click) = "updateEmployee(employee.id)" class = "btn btn-primary"> Update</button>
                <button (click) = "deleteEmployee(employee.id)" class = "btn btn-danger" style="margin-left: 10px"> Delete</button>
                <button (click) = "employeeDetails(employee.id)" class = "btn btn-primary" style="margin-left: 10px"> View</button>
            </td>
        </tr>
    </tbody>
</table>

9. Create Add Employee Component and Template

Path - src/app/create-employee/create-employee.component.ts

CreateEmployeeComponent is used to create and handle a new employee form data. Add the following code to it -
import { Component, OnInit } from '@angular/core';
import { Employee } from '../employee';
import { EmployeeService } from '../employee.service';
import { Router } from '@angular/router';

@Component({
  selector: 'app-create-employee',
  templateUrl: './create-employee.component.html',
  styleUrls: ['./create-employee.component.css']
})
export class CreateEmployeeComponent implements OnInit {

  employee: Employee = new Employee();
  constructor(private employeeService: EmployeeService,
    private router: Router) { }

  ngOnInit(): void {
  }

  saveEmployee(){
    this.employeeService.createEmployee(this.employee).subscribe( data =>{
      console.log(data);
      this.goToEmployeeList();
    },
    error => console.log(error));
  }

  goToEmployeeList(){
    this.router.navigate(['/employees']);
  }
  
  onSubmit(){
    console.log(this.employee);
    this.saveEmployee();
  }
}

Path - src/app/create-employee/create-employee.component.html

The create-employee.component.html shows the add employee HTML form. Add the following code to it -
<div class="row">
    <div class="card col-md-6 offset-md-3 offset-md-3">
        <div class="row">
            <h3 class="text-center"> Create Employee </h3>
            <hr />
            <div class="card-body">
                <form (ngSubmit)="onSubmit()">

                    <div class="form-group">
                        <label> First Name</label>
                        <input type="text" class="form-control" id="firstName" [(ngModel)]="employee.firstName"
                            name="firstName">
                    </div>

                    <div class="form-group">
                        <label> Last Name</label>
                        <input type="text" class="form-control" id="lastName" [(ngModel)]="employee.lastName"
                            name="lastName">
                    </div>

                    <div class="form-group">
                        <label> Email Id</label>
                        <input type="text" class="form-control" id="emailId" [(ngModel)]="employee.emailId"
                            name="emailId">
                    </div>

                    <br />
                    <button class="btn btn-success" type="submit">Submit</button>

                </form>
            </div>
        </div>
    </div>
</div>

10. Create Update Employee Component and Template

Path - src/app/update-employee/update-employee.component.ts

In this UpdateEmployeeComponent, we first get the employee object using REST API and populate it in HTML form via data binding. Users can edit the employee form data and submit the form.
 
Let's add the following code to UpdateEmployeeComponent -
import { Component, OnInit } from '@angular/core';
import { EmployeeService } from '../employee.service';
import { Employee } from '../employee';
import { ActivatedRoute, Router } from '@angular/router';

@Component({
  selector: 'app-update-employee',
  templateUrl: './update-employee.component.html',
  styleUrls: ['./update-employee.component.css']
})
export class UpdateEmployeeComponent implements OnInit {

  id: number;
  employee: Employee = new Employee();
  constructor(private employeeService: EmployeeService,
    private route: ActivatedRoute,
    private router: Router) { }

  ngOnInit(): void {
    this.id = this.route.snapshot.params['id'];

    this.employeeService.getEmployeeById(this.id).subscribe(data => {
      this.employee = data;
    }, error => console.log(error));
  }

  onSubmit(){
    this.employeeService.updateEmployee(this.id, this.employee).subscribe( data =>{
      this.goToEmployeeList();
    }
    , error => console.log(error));
  }

  goToEmployeeList(){
    this.router.navigate(['/employees']);
  }
}

Path - src/app/update-employee/update-employee.component.html

The update-employee.component.html shows the updated employee HTML form. Add the following code to this file -

<div class="row">
    <div class="card col-md-6 offset-md-3 offset-md-3">
        <div class="row">
            <h3 class="text-center"> Update Employee </h3>
            <hr />
            <div class="card-body">
                <form (ngSubmit)="onSubmit()">

                    <div class="form-group">
                        <label> First Name</label>
                        <input type="text" class="form-control" id="firstName" [(ngModel)]="employee.firstName"
                            name="firstName">
                    </div>

                    <div class="form-group">
                        <label> Last Name</label>
                        <input type="text" class="form-control" id="lastName" [(ngModel)]="employee.lastName"
                            name="lastName">
                    </div>

                    <div class="form-group">
                        <label> Email Id</label>
                        <input type="text" class="form-control" id="emailId" [(ngModel)]="employee.emailId"
                            name="emailId">
                    </div>
                    <br />
                    <button class="btn btn-success" type="submit">Submit</button>

                </form>
            </div>
        </div>
    </div>
</div>

11. Create View Employee Details Component and Template

Path - src/app/employee-details/employee-details.component.ts

The EmployeeDetailsComponent component is used to display a particular employee detail. Add the following code to it -
import { Component, OnInit } from '@angular/core';
import { Employee } from '../employee';
import { ActivatedRoute } from '@angular/router';
import { EmployeeService } from '../employee.service';

@Component({
  selector: 'app-employee-details',
  templateUrl: './employee-details.component.html',
  styleUrls: ['./employee-details.component.css']
})
export class EmployeeDetailsComponent implements OnInit {

  id: number
  employee: Employee
  constructor(private route: ActivatedRoute, private employeService: EmployeeService) { }

  ngOnInit(): void {
    this.id = this.route.snapshot.params['id'];

    this.employee = new Employee();
    this.employeService.getEmployeeById(this.id).subscribe( data => {
      this.employee = data;
    });
  }
}

Path - src/app/employee-details/employee-details.component.html

The employee-details.component.html displays a particular employee detail. Add the following code to it -
<h3> View Employee Details</h3>
<div>
    <div>
        <label> <b> First Name: </b></label> {{employee.firstName}}
    </div>
    <div>
        <label> <b> Last Name: </b></label> {{employee.lastName}}
    </div>
    <div>
        <label> <b> Email Id: </b></label> {{employee.emailId}}
    </div>
</div>

12. package.json - Configure Dependencies

Path: /package.json

The package.json file contains project configuration information including package dependencies that get installed when you run npm installFull documentation is available on the npm docs website.
Note that angular version 12.1.0 is in the dependencies section in the below file.
{
  "name": "angular-frontend",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "watch": "ng build --watch --configuration development",
    "test": "ng test"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "~12.1.0-",
    "@angular/common": "~12.1.0-",
    "@angular/compiler": "~12.1.0-",
    "@angular/core": "~12.1.0-",
    "@angular/forms": "~12.1.0-",
    "@angular/platform-browser": "~12.1.0-",
    "@angular/platform-browser-dynamic": "~12.1.0-",
    "@angular/router": "~12.1.0-",
    "bootstrap": "^5.0.2",
    "jquery": "^3.6.0",
    "rxjs": "~6.6.0",
    "tslib": "^2.2.0",
    "zone.js": "~0.11.4"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "~12.1.3",
    "@angular/cli": "~12.1.3",
    "@angular/compiler-cli": "~12.1.0-",
    "@types/jasmine": "~3.8.0",
    "@types/node": "^12.11.1",
    "jasmine-core": "~3.8.0",
    "karma": "~6.3.0",
    "karma-chrome-launcher": "~3.1.0",
    "karma-coverage": "~2.0.3",
    "karma-jasmine": "~4.0.0",
    "karma-jasmine-html-reporter": "~1.7.0",
    "typescript": "~4.3.2"
  }
}

13. App Routing Module

Path: /src/app/app.routing.module.ts

Routing for the Angular app is configured as an array of Routes, each component is mapped to a path so the Angular Router knows which component to display based on the URL in the browser address bar.
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { EmployeeListComponent } from './employee-list/employee-list.component';
import { CreateEmployeeComponent } from './create-employee/create-employee.component';
import { UpdateEmployeeComponent } from './update-employee/update-employee.component';
import { EmployeeDetailsComponent } from './employee-details/employee-details.component';

const routes: Routes = [
  {path: 'employees', component: EmployeeListComponent},
  {path: 'create-employee', component: CreateEmployeeComponent},
  {path: '', redirectTo: 'employees', pathMatch: 'full'},
  {path: 'update-employee/:id', component: UpdateEmployeeComponent},
  {path: 'employee-details/:id', component: EmployeeDetailsComponent}
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],                                                                                                                                                                                                                                                                                                          
  exports: [RouterModule]
})
export class AppRoutingModule { }

14. App Component

Path: /src/app/app.component.ts

The app component is the root component of the application, it defines the root tag of the app as with the selector property of the @Component decorator.
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'Angular + Spring Boot CRUD Full Stack App';
}

15. App Component Template

Path: /src/app/app.component.html

Defines the HTML template associated with the root AppComponent.
<nav class="navbar navbar-expand-sm bg-primary navbar-dark">
    <ul class = "navbar-nav">
        <li class = "nav-item">
            <a routerLink="employees" routerLinkActive="active" class="nav-link" >Employee List</a>
        </li>
        <li class = "nav-item">
            <a routerLink="create-employee" routerLinkActive="active" class="nav-link" >Add Employee</a>
        </li>
    </ul>
</nav>

<h1 class="text-center"> {{title}} </h1>
<div class = "container">
    <router-outlet></router-outlet>
</div>
<footer class = "footer">
    <div class = "container">
        <span>All Rights Reserved 2020 @JavaGuides</span>
    </div>
</footer>

16. App Module

Path: /src/app/app.module.ts

Defines the root module, named AppModule, that tells Angular how to assemble the application. Initially declares only the AppComponent. As you add more components to the app, they must be declared here.
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http'
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { EmployeeListComponent } from './employee-list/employee-list.component';
import { CreateEmployeeComponent } from './create-employee/create-employee.component';
import { FormsModule} from '@angular/forms';
import { UpdateEmployeeComponent } from './update-employee/update-employee.component';
import { EmployeeDetailsComponent } from './employee-details/employee-details.component'

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

17. Running Angular Client Application

Let's run the above developed Angular App with a command:
ng serve
By default, the Angular app runs on 4200 port but you can change the default port with the following command:
ng serve --port 4201

Demo of Complete Full-stack Application

Make sure that both (Spring boot and Angular) applications are up and running.

Hit http://localhost:4200 link in a browser that will host this Angular CRUD app.

Below are the screenshots shows the UI of our Employee Management System App.

Employee List Page

Add Employee Page

Update Employee Page

Conclusion

In this full-stack tutorial, we have seen how to develop a CRUD (Create, Read, Update, Delete) web application using Angular 12 as a front-end and Spring boot + PostgreSQL as a backend.

The source code of this tutorial is hosted on our GitHub repository at https://github.com/sourcecodeexamples/angular-springboot-crud-example/tree/main/angular-frontend

Comments