Introduction
In today's fast-paced digital landscape, full-stack development has become an essential skill for any aspiring developer. With the rise of modern web applications, the demand for efficient and scalable solutions has increased significantly. Two of the most popular technologies in the industry today are Angular and Spring Boot. Angular, a JavaScript framework, is ideal for building complex and dynamic user interfaces, while Spring Boot, a Java-based framework, is perfect for building robust and scalable backend services.
In this tutorial, we will provide a step-by-step guide on how to integrate Angular and Spring Boot with CRUD (Create, Read, Update, Delete) operations. By the end of this tutorial, you will have a comprehensive understanding of how to build a full-stack application using these two powerful technologies.
Installations
To make the process faster let's install the spring boot tool to our Eclipse IDE.
Go to Help > Eclipse Marketplace.
Then Search for "spring boot tool" and install it to your Eclipse IDE.
Setting Up Project
Once done with the installation of the Spring Boot Tool.
Let's create a Spring Boot application using it.
Go to File > New > Others. and search for "Spring Boot"
select the "Spring Starter Project".
Then select these options as required. we are using Maven and Java version 17 with packaging type Jar.
Then we are adding project dependencies.
1. Spring Boot DevTools
This dependency provides additional development-time features, such as:
Automatic restart of the application when code changes are detected.
Live reload of static resources (e.g., HTML, CSS, JavaScript files).
Support for remote debugging.
Ability to toggle debug logging on and off.
This dependency is optional, and you can remove it if you don't need these features.
2. Spring Data JPA
This dependency provides support for Java Persistence API (JPA) in a Spring Boot application.
It enables you to use JPA to interact with a relational database, such as MySQL.
This dependency includes:
Hibernate as the JPA provider.
Support for database transactions.
Automatic creation of database schema based on entity classes.
Support for CRUD (Create, Read, Update, Delete) operations.
3. MySQL Driver
This dependency provides the MySQL JDBC driver, which allows your application to connect to a MySQL database.
This dependency is required if you want to use a MySQL database with your Spring Boot application.
4. Spring Web
This dependency provides support for building web applications using Spring Boot.
It includes:
Support for building RESTful web services using Spring MVC.
Support for HTTP requests and responses.
Automatic configuration of web-related beans, such as the DispatcherServlet.
Support for web-related annotations, such as @RestController and @RequestMapping.
Hit Finish and our project is ready.
Now create an empty folder on your chosen path and open that folder in VScode.
Go to Terminal and hit New Terminal.
Now at the bottom of VScode, you will see a new terming.
Write the following command to create a new angular application. (Make sure you have installed node and angular CLI).
ng new <<Application Name>> --no-standalone
Use --no-standalone if you are using angular version 17+
Now it will ask for a few options select
CSS, and routing option Yes
and our project is ready.
Setup REST API
Now in Eclipse create 4 packages as follows.
controller, exception, model, repository.
Now go to your project src/main/resources and open application.properties
and add some properties as follows.
spring.datasource.url=jdbc:mysql://localhost:3306/employee_management?useSSL=false
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root123
spring.jpa.hibernate.ddl-auto = update
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL8Dialect
Now in the model package create a class name Employee
and make it an Entity using annotations and create getter-setter Methods.
@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) {
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;
}
}
Now in the repository package create an interface named EmployeeRepository and extend it with JpaRepository as follows.
@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long>{}
We don't need to write anything inside as everything is in the parent interface.
Now create a custom exception class in the exception package.
@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException{
private static final long serialVersionUID = 1L;
public ResourceNotFoundException() {
super();
}
public ResourceNotFoundException(String message) {
super(message);
}
}
Now create an employee controller.
Use the following annotations to define your controller as a rest controller and map it with the URL.
@RestController
@RequestMapping("/api/v1/")
Create an object of EmployeeRpository and use annotation to Auto wire it as follows.
@Autowired
private EmployeeRepository employeeRepository;
Create the getAllEmployees Method that returns a list of employees from the database for that we will use the GetMapping annotation.
@GetMapping("/employees")
public List<Employee> getAllEmployees() {
return employeeRepository.findAll();
}
Same way create method name createEmployee with PostMapping annotation. and for the parameter, we will use RequestBody annotation for the object of Employee Entity so it can fill data into that object.
@PostMapping("/employees")
public Employee createEmployee(@RequestBody Employee employee) {
return employeeRepository.save(employee);
}
For Updating employee data we will use the UpdateEmployeeById method and we will use the PutMapping annotation with same RequestBody and PathVariable annotations. PathVariable annotation will fill the value with the value we send with the URL.
@PutMapping("/employees/{id}")
public ResponseEntity<Employee> updateEmployeeById(@PathVariable Long id, @RequestBody Employee requestedEmployee) {
Employee employee = employeeRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Employee with id = " + id + " does not exists"));
employee.setFirstName(requestedEmployee.getFirstName());
employee.setLastName(requestedEmployee.getLastName());
employee.setEmailId(requestedEmployee.getEmailId());
return ResponseEntity.ok(employeeRepository.save(employee)); // required to send request status as well.
}
Also, you can see we are returning a response status here.
Deletion is somewhat similar to updation which is as follows.
@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);
}
Now we are all set to run our REST API.
Setup Angular Project.
Now let's open VScode and open our angular project in it.
Go to the terminal and write.
cd <<Applocation name>>
Now we are creating some Components. for that we have command.
ng g c <<Component Name>>
where g is for Generate, and c is for Component.
Using this same command create 3 components
employee-list, update-employee, and create employee.
Now let's create an Employee Class in this project similar to the entity in REST API.
To create a class use the command...
ng g class <<Class Name>>
Open that class file and create members as.
export class Employee {
id: number;
firstName: string;
lastName: string;
emailId: string;
}
now go to employee-list.component.html in the employee-list folder and write this code.
<h2> Employee List</h2>
<table class = "table table-striped">
<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-info"> Update</button>
<button (click) = "deleteEmployee(employee.id)" class = "btn btn-danger" style="margin-left: 10px"> Delete</button>
</td>
</tr>
</tbody>
</table>
NOTE: I have used Bootstrap CDN on the index.html page. so I can use it in all the components.
Now to communicate with REST API we need services. so let's create one.
ng g s <<Service name>>
Use this command to create a service.
Now open that service file and write as follows.
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);
}
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}`);
}
Now go to app.module.ts file and replace your import section and provider. and import necessary files.
imports: [
BrowserModule,
AppRoutingModule,
FormsModule // ngModel
// HttpClientModule
],
providers: [provideHttpClient() /*http clint provider*/],
NOTE: HttpClientModule is been deprecated so we have to use provideHttpClient provider
Also, we have imported FormModule for using ngModel for 2 way data binding.
Now write this to employee-list.component.ts file.
employees: Employee[];
constructor(private employeeService: EmployeeService,
private router: Router) { }
ngOnInit(): void {
this.getEmployees();
}
private getEmployees(){
this.employeeService.getEmployeesList().subscribe(data => {
this.employees = data;
});
}
updateEmployee(id: number){
this.router.navigate(['update-employee', id]);
}
deleteEmployee(id: number){
this.employeeService.deleteEmployee(id).subscribe( data => {
console.log(data);
this.getEmployees();
})
}
Now let's set up the navigations.
Go to app-routing.module.ts. and replace routes to
const routes: Routes = [
{path: 'employees', component: EmployeeListComponent},
{path: 'create-employee', component: CreateEmployeeComponent},
{path: '', redirectTo: 'employees', pathMatch: 'full'},
{path: 'update-employee/:id', component: UpdateEmployeeComponent},
];
Add below code to 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>
Now follow the file Names.
create-employee.component.html
<div class="col-md-6 offset-md-3">
<h3> Create Employee </h3>
<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>
<button class = "btn btn-success" type ="submit">Submit</button>
</form>
</div>
create-employee.component.ts
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();
}
update-employee.component.html
<div class="col-md-6 offset-md-3">
<h3> Update Employee </h3>
<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>
<button class = "btn btn-success" type ="submit">Submit</button>
</form>
</div>
NOTE: we can use Create employee form but to make it simple we are creating new one
update-employee.component.ts
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']);
}
Now we are done.
Try running both applications and make sure you run API first and then Angular application. To run Angular app use.
ng serve
if you get an error of running API on a different port "origin" error.
go to your Employee Controller and add an annotation
@CrossOrigin(origins = "*")
At class level.
Congratulations! You have successfully completed the tutorial on integrating Angular and Spring Boot with CRUD operations.
In this tutorial, you learned how to:
Set up a Spring Boot project with Eclipse and create a REST API for CRUD operations.
Create an Angular project with VSCode and set up the necessary components, services, and modules.
Communicate with the REST API using the Angular service and HttpClient.
Implement CRUD operations in the Angular application, including listing, creating, updating, and deleting employees.
By following this tutorial, you have gained hands-on experience in building a full-stack application using Angular and Spring Boot. You can now apply this knowledge to build more complex and scalable applications.
Remember to run both applications simultaneously, with the Spring Boot API running on port 8080 and the Angular application running on port 4200 (default port for ng serve). If you encounter any issues with CORS, don't forget to add the @CrossOrigin annotation to your Spring Boot controller.
Thank you for following this tutorial, and happy coding!
Comments