🎓 Top 15 Udemy Courses (80-90% Discount): My Udemy Courses - Ramesh Fadatare — All my Udemy courses are real-time and project oriented courses.
▶️ Subscribe to My YouTube Channel (178K+ subscribers): Java Guides on YouTube
▶️ For AI, ChatGPT, Web, Tech, and Generative AI, subscribe to another channel: Ramesh Fadatare on YouTube
In this full-stack tutorial, we will learn how to develop a CRUD (Create, Read, Update, Delete) web application using Angular 12 as a front-end and Spring boot as a backend.
Angular is a platform and framework for building single-page client applications using HTML and TypeScript. Angular is written in TypeScript.Angular Spring boot Full-Stack Architecture
What we will build?
- springboot-backend: This project is used to develop CRUD RESTFul APIs for a simple Employee Management System using Spring Boot, JPA, and MySQL as a database.
- angular-frontend: This project is used to develop single page application using Angular 12 as front-end technology. This Angular application consumes CRUD Restful APIs developed and exposed by a springboot-backend project.
Prerequisites
- Basic familiarity with HTML & CSS
- Basic knowledge of JavaScript and programming
- Spring Boot Basics
- Angular basics
- Node.js and npm installed globally
Tools and technologies used
Server-side technologies
- Spring Boot
- JDK - 1.8 or later
- Spring Framework
- Spring Data JPA (Hibernate)
Front end technologies
- Angular (Latest version as of now)
- Bootstrap 4
- Node and NPM
- JQuery
Tools
- Maven - 3.2+
- IDE - Eclipse or Spring Tool Suite (STS) // Spring boot API development
- Visual Studio 2017 // Angular App development
- Angular CLI
Let's first develop the Spring boot backend and build CRUD REST APIs.
1. Develop Spring Boot Backend Application
Step1: Create a Spring Boot Application
>> Create Spring Boot Project in Spring Tool Suite [STS]
Step 2: Maven dependencies
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
3. Configuring Database (H2 or MySQL)
H2 Database Configuration
<dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency>
If you use the H2 database then we no need to configure the database-related properties in the application.properties file.
If you use H2 in-memory database then you no need to create a database and tables, spring boot automatically do it for you.
MySQL Database Configuration
Step 1: Replace the H2 database dependency with MySQL dependency:
<dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency>
spring.datasource.url = jdbc:mysql://localhost:3306/EMS?useSSL=false spring.datasource.username = root spring.datasource.password = root ## Hibernate Properties # The SQL dialect makes Hibernate generate better SQL for the chosen database spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect # Hibernate ddl auto (create, create-drop, validate, update) spring.jpa.hibernate.ddl-auto = update server.servlet.context-path=/springboot-crud-rest
create database EMS
4. Create JPA Entity - Employee.java
package net.javaguides.springboot.model;
import jakarta.persistence.*;
@Entity
@Table(name = "employees")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
@Column(name = "email_id")
private String emailId;
public Employee() {
}
public Employee(String firstName, String lastName, String emailId) {
super();
this.firstName = firstName;
this.lastName = lastName;
this.emailId = emailId;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmailId() {
return emailId;
}
public void setEmailId(String emailId) {
this.emailId = emailId;
}
}
5. Create a Spring Data Repository - EmployeeRepository.java
package net.javaguides.springboot.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import net.javaguides.springboot.model.Employee;
@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long>{
}
6. Create Custom Exception
package net.javaguides.springboot.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException{
private static final long serialVersionUID = 1L;
public ResourceNotFoundException(String message) {
super(message);
}
}
7. Create Spring Rest Controller - EmployeeController.java
package net.javaguides.springboot.controller;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import net.javaguides.springboot.exception.ResourceNotFoundException;
import net.javaguides.springboot.model.Employee;
import net.javaguides.springboot.repository.EmployeeRepository;
@CrossOrigin(origins = "http://localhost:4200")
@RestController
@RequestMapping("/api/v1/")
public class EmployeeController {
@Autowired
private EmployeeRepository employeeRepository;
// get all employees
@GetMapping("/employees")
public List<Employee> getAllEmployees(){
return employeeRepository.findAll();
}
// create employee rest api
@PostMapping("/employees")
public Employee createEmployee(@RequestBody Employee employee) {
return employeeRepository.save(employee);
}
// get employee by id rest api
@GetMapping("/employees/{id}")
public ResponseEntity<Employee> getEmployeeById(@PathVariable Long id) {
Employee employee = employeeRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Employee not exist with id :" + id));
return ResponseEntity.ok(employee);
}
// update employee rest api
@PutMapping("/employees/{id}")
public ResponseEntity<Employee> updateEmployee(@PathVariable Long id, @RequestBody Employee employeeDetails){
Employee employee = employeeRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Employee not exist with id :" + id));
employee.setFirstName(employeeDetails.getFirstName());
employee.setLastName(employeeDetails.getLastName());
employee.setEmailId(employeeDetails.getEmailId());
Employee updatedEmployee = employeeRepository.save(employee);
return ResponseEntity.ok(updatedEmployee);
}
// delete employee rest api
@DeleteMapping("/employees/{id}")
public ResponseEntity<Map<String, Boolean>> deleteEmployee(@PathVariable Long id){
Employee employee = employeeRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Employee not exist with id :" + id));
employeeRepository.delete(employee);
Map<String, Boolean> response = new HashMap<>();
response.put("deleted", Boolean.TRUE);
return ResponseEntity.ok(response);
}
}
Enable CORS on the Server
To enable CORS on the server, add a @CrossOrigin annotation to the EmployeeController:@CrossOrigin(origins = "http://localhost:4200")
@RestController
@RequestMapping("/api/v1/")
public class EmployeeController {
// ..
}
@CrossOrigin(origins = "http://localhost:4200")
@RestController
@RequestMapping("/api/v1/")
public class EmployeeController {
// ..
}
8. Running Application
package net.javaguides.springboot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringbootBackendApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootBackendApplication.class, args);
}
}
9. Testing REST APIs
Get All Employees:
http://localhost:8080/api/v1/employees
Get Employee By Id:
http://localhost:8080/api/v1/employees/{employeeId}
Create Employee:
http://localhost:8080/api/v1/employees
Update Employee
http://localhost:8080/api/v1/employees/{employeeId}
Delete Employee By Id:
http://localhost:8080/api/v1/employees/{employeeId}
This completes the development of Spring boot CRUD Rest APIs.
2. Develop Angular Frontend Application
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
npm install -g @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
ng new angular-frontend
3. Identify Components, Services, and Modules
Components
- create-employee
- update-employee
- employee-list
- employee-details
Services
- employee.service.ts - Service for HTTP Client methods
Modules
- FormsModule
- HttpClientModule
- AppRoutingModule
Employee Class (Typescript class)
- employee.ts: class Employee (id, firstName, lastName, emailId)
4. Create Angular Components and Service Classes using Angular CLI
– 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
npm install bootstrap jquery --save
...
"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"
]
...
/* 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)
export class Employee {
id: number;
firstName: string;
lastName: string;
emailId: string;
}
7. Create Employee Service - REST Client
Path - src/app/employee.service.tsThe 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.
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}`);
}
}
8. Creating Employee List Component and Template
Path - src/app/employee-list/employee-list.component.ts
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
<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
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
<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
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
<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
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
<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
{
"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
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
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
<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
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
ng serve
ng serve --port 4201
Comments
Post a Comment
Leave Comment