Hello Java programmers, if you are looking for a full-stack Java project with Spring Boot and Angular then you have come to the right place. Earlier, I have shared best Spring Boot courses, books as well as Spring boot + Reactjs project and In this tutorial, we are going to discuss how to create your first application using spring boot and angular. So we are using a simple crud application to build the application here. Here we create a School Classroom Dashboard application that can insert, update, delete and search students. The frontend of the application will be handled by angular and the backend of the system is using the spring boot. This is also a great Spring boot project beginners to do learn Spring boot better. So let's have a look into this.
Tools and technologies which is used in this application.
- You can use any IDE to develop the backend(spring boot) and the frontend of the application.
- Server: Apache Tomcat
- Spring Boot 2
- Angular 11
- H2 Database
So let's create the spring boot backend of the system first.
The Spring Boot Application
Here, the REST API is used to communicate with the frontend(angular) of the application. Before you start programming, you need to have a better structure of the project. So below is the project structure which is used in this application.
This application is used to store some data in the in-memory database of H2 and fetch those data. So below are the maven dependencies in the pom.xml file which is used in this example.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.student</groupId>
<artifactId>crudapp</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>crudapp</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
The Application.properties file
SpringApplication will load properties from application.properties files in the following locations and add them to the Spring Environment:
1. config subdirectory of the current directory.
2. The current directory
3. A classpath /config package
4. The classpath root
Below is the used applicaiton.properties file in this Student Crud application.
server.port=8090
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
spring.datasource.url=jdbc:h2:mem:cmpe172
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
JPA Entity Class
Below is the used JPA entity class in this application. This is responsible for modeling Students.
package com.student.crudapp.model;
import javax.persistence.*;
@Entity
@Table(name = "STUDENT")
public class Student {
@Column(name = "id")
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private int id;
@Column(name = "name")
private String name;
@Column(name = "email")
private String email;
@Column(name = "grade")
private String grade;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getGrade() {
return grade;
}
public void setGrade(String grade) {
this.grade = grade;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
", grade='" + grade + '\'' +
'}';
}
}
The StudentRepository Interface
As we need to stick with the crud functionality of our system, we need to configure our StudentRepository interface as a Crud repository as below.
package com.student.crudapp.repository;
import com.student.crudapp.model.Student;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface StudentRepository extends JpaRepository<Student, Integer> {
List<Student> findAll();
Student findById(int id);
}
The StudentController Class
Below is the StudentController class which is used in the application. There, we implement the addStudent, findStudent, getAllStudents, updateStudent and deleteStudent methods which are communicating with the H2 database in order to store them in the in-memory database.
package com.student.crudapp.controller;
import com.student.crudapp.model.Student;
import com.student.crudapp.repository.StudentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Controller
@CrossOrigin(origins = "http://localhost:8090")
public class StudentController {
@Autowired
StudentRepository studentRepository;
//check the api's working correctly api
@RequestMapping(value="/ping", method=RequestMethod.GET)
@ResponseBody
public String healthCheck() {
return "This is working well";
}
@RequestMapping(value="/students", method=RequestMethod.GET)
@ResponseBody
public List<Student> getAllStudents() {
return studentRepository.findAll();
}
@RequestMapping(value="/student", method=RequestMethod.POST)
@ResponseBody
public Student addStudent(Student student) {
return studentRepository.save(student);
}
@RequestMapping(value="/findstudent", method = RequestMethod.GET)
@ResponseBody
public Student findStudent(@RequestParam("studentId") int studentId) {
return studentRepository.findById(studentId);
}
@RequestMapping(value= "/updatestudent", method = RequestMethod.GET)
@ResponseBody
public Student updateStudent(@RequestBody Student student){
return studentRepository.save(student);
}
@RequestMapping(value="/deletestudent", method = RequestMethod.GET)
@ResponseBody
public int deleteStudent(@RequestParam("studentId") int studentId) {
return studentRepository.deleteById(studentId);
}
}
In the above controller, we used the @CrossOrigin annotation, in order to enable Cross-Origin Resource Sharing (CORS) on the server.
You think this is unnecessary, but the thing is we're deploying our Angular frontend to http://localhost:4200, and our Boot backend to http://localhost:8090, the browser would otherwise deny requests from one to the other. the server.
So below are the created API's in order to deal with frontend of the application.
1. Add a new Student (POST request)
http://localhost:8090/student
{
"name": "Test",
"email": "test@gmail.com",
"grade": "05"
}
2. Get all students (GET request)
http://localhost:8090/students
3. Find specific student(GET request)
http://localhost:8090/findstudent?studentId=1
4. Update student(GET Request)
http://localhost:8090/updatestudent
{
"id": 1,
"name": "Testupdated",
"email": "testupdated@gmail.com",
"grade": "05"
}
5. Delete student(GET request)
http://localhost:8090/deletestudent?studentId=1
Here is the screenshot of the H2 database that we have created.
The Angular Application.
With the spring boot application running on port number 8090, we can create a simple angular application that is capable of consuming the REST controller API.
Angular CLI Installation
We are using the Angular CLI to create our application as it is Angular CLI is a really useful tool because it allows us to create a full Angular project from start with only a few commands, generating components, services, classes, and interfaces.
npm install -g @angular/cli
Project Scaffolding with Angular CLI
Open a command console, then navigate to the folder where we want our application to be created, and type the command:
ng new my-first-project
The Angular Application's Entry Point
TypeScript, a typed superset of JavaScript that compiles to normal JavaScript, is used in Angular's application files. Any Angular application, on the other hand, starts with a plain old index.html file. Add the bootstrap class to this file in order to get the support.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>MyFirstProject</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<!-- CSS only -->
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
crossorigin="anonymous">
</head>
<body>
<!-- JavaScript Bundle with Popper -->
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
crossorigin="anonymous"></script>
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css"
integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ"
crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.1.1.slim.min.js"
integrity="sha384-A7FZj7v+d/sdmMqp/nOQwliLvUsJfDHW+k9Omg/a/EheAdgtzNs3hpfag6Ed950n"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js"
integrity="sha384-DztdAPBWPRXSA/3eYEEUWrWCy7G5KFbe8fFjk5JAIxUYHKkDx6Qin1DkWx51bBrb"
crossorigin="anonymous"></script>
<script
src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js"
integrity="sha384-vBWWzlZJ8ea9aCX4pEW3rVHjgjt7zpkNpZk+02D9phzyeVkE+jo0ieGizqPLForn"
crossorigin="anonymous"></script>
<app-root></app-root>
</body>
</html>
<app-root> is the root selector that
Angular uses for rendering the application's root component.
The app.component.ts Root Component.
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title: string;
constructor() {
this.title = 'Spring Boot application';
}
}
The constructor sets the field's value to a string, which is similar to how we do it in Java.
The @Component metadata marker or decorator is the most important aspect, as it defines three elements:
Selector - The HTML selector is used to tie the component to the HTML template file template.
Url – the URL to the HTML template file for the component style.
URLs - One or more CSS files connected with the component are referred.
As expected, we can use the app.component.html and app.component.css files to define the HTML template and the CSS styles of the root component.
So let's move to our application. So you need to create a component called, student-dashboard by the following command.
ng g c student-dashboard
The ngOnInit interface is implemented by an empty component class generated by Angular CLI. The interface declares the ngOnInit() function, which Angular runs when it has done instantiating and calling the constructor of the implementing class.
Here is the updated, student-dashboard.component.ts file which is dealing with an HTML page.
import { Component, OnInit } from '@angular/core';
import { FormBuilder,FormGroup} from '@angular/forms'
import { ApiService } from '../shared/api.service';
import { StudentModel } from './student-dashboard.model';
@Component({
selector: 'app-student-dashboard',
templateUrl: './student-dashboard.component.html',
styleUrls: ['./student-dashboard.component.css']
})
export class StudentDashboardComponent implements OnInit {
formValue !: FormGroup;
studentModelOb : StudentModel = new StudentModel();
studentData !: any;
constructor(private formbuilder: FormBuilder,
private api : ApiService){}
ngOnInit(): void {
this.formValue = this.formbuilder.group({
name: [''],
email: [''],
grade: [''],
})
this.getAllStudents();
}
postStudentDEtails(){
console.log(this.formValue.value.name);
console.log(this.formValue.value.grade);
this.studentModelOb.name = this.formValue.value.name;
this.studentModelOb.grade = this.formValue.value.grade;
this.studentModelOb.email = this.formValue.value.email;
console.log(this.studentModelOb);
this.api.postStudent(this.studentModelOb)
.subscribe(res=>{
console.log(res);
alert("Student added succesfully")
let ref = document.getElementById('cancel')
ref?.click();
this.formValue.reset();
this.getAllStudents();
},
err=>{
alert("Something went wrong");
})
}
getAllStudents(){
this.api.getStudent()
.subscribe(res=>{
this.studentData = res;
})
}
deleteStudent(row: any){
this.api.deleteStudent(row.id)
.subscribe(res =>{
alert("Student Deleted !")
this.getAllStudents();
})
}
onEdit(row: any){
this.studentModelOb.id = row.id;
this.formValue.controls['name'].setValue(row.name);
this.formValue.controls['grade'].setValue(row.grade);
this.formValue.controls['email'].setValue(row.email);
}
updateStudentDetails(){
this.studentModelOb.name = this.formValue.value.name;
this.studentModelOb.grade = this.formValue.value.grade;
this.studentModelOb.email = this.formValue.value.email;
this.api.updateStudent(this.studentModelOb).
subscribe(res=>{
alert("Student updated Successfully");
console.log(res);
let ref = document.getElementById('cancel')
ref?.click();
this.formValue.reset();
this.getAllStudents();
});
}
}
I
The student-dahsboard.component.html file
n addition, we need to edit the component's HTML file, student-dashboard.component.html to create the table that displays the list of entities and also modal to create add, update and delete data.
<nav class="navbar navbar-light bg-primary">
<div class="container-fluid">
<h1 style="color: aliceblue">Angular Crud </h1>
<div class="d-flex">
<!-- Button trigger modal -->
<button type="button" class="btn btn-success" data-toggle="modal"
data-target="#exampleModalLong">
Add Student
</button>
</div>
</div>
</nav>
<table class="table mt-3">
<thead>
<tr>
<th scope="col">Student ID</th>
<th scope="col">Name</th>
<th scope="col">Email</th>
<th scope="col">Grade</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of studentData">
<td>{{row.id}}</td>
<td>{{row.name}}</td>
<td>{{row.email}}</td>
<td>{{row.grade}}</td>
<td>
<button class="btn btn-info" (click) = "onEdit(row)"
data-target="#exampleModalLong" data-toggle="modal">Edit</button>
<button (click) = "deleteStudent(row)"
class="btn btn-danger mx-3">Delete</button>
</td>
</tr>
</tbody>
</table>
<!-- Modal -->
<div class="modal fade" id="exampleModalLong" tabindex="-1" role="dialog"
aria-labelledby="exampleModalLongTitle" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLongTitle">Student Details</h5>
<button type="button" class="close" data-dismiss="modal"
aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form [formGroup]="formValue">
<div class="form-group">
<label for="name">Name</label>
<input type="text" formControlName="name"
class="form-control"id="name" aria-describedby="nameHelp" placeholder="First Name">
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" formControlName="email"
class="form-control" id="email" placeholder="Email">
</div>
<div class="form-group">
<label for="grade">Grade</label>
<input type="text" formControlName="grade"
class="form-control" id="grade" placeholder="Grade">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary"
data-dismiss="modal">Close</button>
<button type="button" (click)="postStudentDEtails()"
class="btn btn-primary">Add</button>
<button type="button" (click)="updateStudentDetails()"
class="btn btn-primary">Update</button>
</div>
</div>
</div>
</div>
The use of the *ngFor directive should be noted. A repeater is a directive that may be used to iterate over the values of a variable and iteratively render HTML components. We utilized it to dynamically render the table's rows in this situation.
The use of api.service.ts
This file is used to create an endpoint which is dealing with the API of the application. So let's have a look into this. To create this file we used the following command.
ng g s shared/api
This will create the api.service.ts file which is able to handle the API requests.
import { Injectable } from '@angular/core';
import {HttpClient, HttpClientModule} from '@angular/common/http';
import {map} from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class ApiService {
constructor(private http: HttpClient) { }
postStudent(data : any){
return this.http.post<any>("http://localhost:8090/student", data)
.pipe(map((res: any)=>{
return res;
}));
}
getStudent(){
return this.http.get<any>("http://localhost:8090/students")
.pipe(map((res:any)=>{
console.log(res)
return res;
}))
}
updateStudent(data: any){
return this.http.put<any>('http://localhost:8090/updatestudent',data)
.pipe(map((res:any)=>{
return res;
}))
}
deleteStudent(id: any){
return this.http.delete<any>('http://localhost:8090/deletestudent', id)
.pipe(map((res:any)=>{
return res;
}))
}
}
The ngModel directive enables two-way data binding between form controls and the User class, the client-side domain model.
This means that information submitted in the form of input fields will flow to the model and vice versa. DOM manipulation will quickly reflect changes in both items.
Additionally, by adding separate CSS classes and DOM properties to each control, ngModel allows us to keep track of the state of each form control and do client-side validation.
We just used the properties applied to the form controls in the above HTML file to display an alert box when the values in the form were modified.
The app.routing.module.ts file
Next, we need to edit the app.module.ts file, so Angular can import all the required modules, components, and services.
\
For building and injecting the UserService class, we must indicate the provider we'll use. Angular will be unable to inject it into component classes if this is not done:
import { HttpClient, HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { StudentDashboardComponent }
from './student-dashboard/student-dashboard.component';
@NgModule({
declarations: [
AppComponent,
StudentDashboardComponent
],
imports: [
BrowserModule,
AppRoutingModule,
ReactiveFormsModule,
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
A route is composed of two parts:
- Path – a string that matches the URL in the browser address bar
- Component – the component to create when the route is active (navigated)
The app.component.html file
Here we add the component which is created to this HTML file which is used to integrate the components.
<app-student-dashboard></app-student-dashboard>
Running the application.
First, we need to start by running the Spring Boot application to make sure the REST service is up and running and ready to receive requests. Then that we need to run the following command on the project folder command-line interface.
ng serve
This will start the live development server and also open the browser at http://localhost:4200/
So let's have a look at the frontend interfaces of the system.
Conclusion.
In this article, we discussed how to create a simple crud application using spring boot and angular. There are a lot of things to learn and do with these technologies and you will end up with a good programmer.
Other
Java and Spring articles you may like
- 15 Microservices Interview questions (answers)
- 5 courses to learn Spring Boot and Spring Cloud ( courses)
- 15 Spring Cloud Interview Questions for Java developers (answers)
- 5 Courses to learn Spring Cloud and Microservices (courses)
- 15 Spring Data JPA Interview Questions with answers (questions)
- 10 Tools Java Developers use in their day-to-day life (tools)
- 5 Course to Master Spring Boot online (courses)
- 10 Courses to learn Spring Security with OAuth 2 (courses)
- 10 Advanced Spring Boot Courses for Java Programmers (courses)
- Top 5 Books and Courses to learn RESTful Web Service (books)
- 5 Spring Boot Annotations for full-stack Java developers (tutorial)
- 3 ways to change Tomcat port in Spring Boot (tutorial)
- 10 Spring MVC annotations Java developers should learn (annotations)
- Top 5 Courses to learn Microservices in Java? (courses)
- 3 Best Practices Java Programmers can learn from Spring (best practices)
Thanks for reading this article so far; if you find these
Spring boot Actuator interview questions and answers useful, please share them with your friends and colleagues.
P. S. - If you are new to Spring Boot and want to learn about Spring Boot and look for a free Spring Boot online course, I also recommend you join the
Introducing Spring Boot (FREE ) class by Dan Vega on Udemy. It's one of the best free courses to learn Spring Boot for Java developers.
Open api + openapi-generator would make a nice addition to this great recipe
ReplyDelete