Spring Boot is a popular Java framework used to build microservice applications, REST APIs, etc. It is one of the widely used frameworks for developing microservices.

Angular is a popular javascript front end development framework used to build web applications. Angular provides rich support to create a good user experience and provides tools that help in simplifying large and complex web application development.

In this article, we will learn how to build a simple CRUD application using Angular front end and Spring boot as a backend framework.

We will create a simple-looking web application where users can add student details, update or delete them from the system. We will also display a table that shows all available student details in the database.

Spring-boot-angular-crud-example

Below are the version details used in this example:

  • Angular: 10.0.14
  • Angular CLI: 10.0.8
  • Spring boot: 2.3.3.RELEASE
  • Embedded H2 database.
  • Spring Tool Suite IDE.
  • Visual Studio Code.

Spring Boot Application

We will create RESTful endpoints that support CRUD operation with the help of Spring Boot.

The RESTful endpoint will have REST APIs to create a new student, update student information, get all available students, and delete the student entity from the database.

We will use an embedded H2 database in this example.

Create a Spring Boot application

Let us create a new spring boot application with spring-boot-starter-web, spring-boot-starter-data-jpa, h2, and lombok maven dependencies.

Below is the required maven dependency list.

<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>com.h2database</groupId>
	<artifactId>h2</artifactId>
	<scope>runtime</scope>
</dependency>
<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
	<optional>true</optional>
</dependency>
  • spring-boot-starter-web: Provides support for creating a web application and REST APIs.
  • spring-boot-starter-data-jpa: Provides support for database related operation.
  • h2: Adds an embedded H2 database during application runtime.
  • Lombok: A utility that helps to reduce boilerplate code.

Add database related configurations

Update the application.properties configuration file under the Spring Boot applications src/main/resources/ directory with below content.

spring.datasource.url=jdbc:h2:~/asbdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.data-username=sa
spring.datasource.data-password=
spring.jpa.show-sql=true

We have added the required configurations to connect to the embedded H2 database. The above properties define the data source URL, username, and password(optional).

We have set the spring.jpa.show-sql property’s value to true. This configuration makes the application print the generated SQL queries in the application console.

Create Student entity class

Create a Student.java entity class and add the below content.

The entity class contains an Id(auto-generated), name, and grade of the student. If you are new to JPA, I recommend you to read this introductory post.

package com.asbnotebook.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

import lombok.Getter;
import lombok.Setter;

@Entity
@Table(name = "STUDENT")
@Getter
@Setter
public class Student {

	@Id
	@Column(name = "ID")
	@GeneratedValue(strategy = GenerationType.AUTO)
	private Long id;

	@Column(name = "NAME")
	private String name;

	@Column(name = "GRADE")
	private Integer grade;
}

The STUDENT table will be auto-generated by JPA on application startup. Spring boot uses the default DDL strategy(create-drop) for the embedded databases.

Create the JPA repository class

Create an interface called StudentRepository.java and extend the JpaRepository interface as given below.

With this, the spring data jpa library gives CRUD operation support to our repository interface.

package com.asbnotebook.repo;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.asbnotebook.model.Student;

@Repository
public interface StudentRepository extends JpaRepository<Student, Long>{

}

Create the REST controller endpoints

The final step is to create the REST endpoints that support the CRUD operation on the Student entity.

Create a class called StudentController.java and add the below content.

package com.asbnotebook.controller;

import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
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.RestController;

import com.asbnotebook.model.Student;
import com.asbnotebook.repo.StudentRepository;

@CrossOrigin(origins = {"http://localhost:4200"})
@RestController
public class StudentController {

	@Autowired
	private StudentRepository studentRepository;

	@GetMapping("/students/")
	public ResponseEntity<List<Student>> getStudents() {
		List<Student> students = studentRepository.findAll();
		return new ResponseEntity<>(students, HttpStatus.OK);
	}

	@PostMapping("/students/")
	public ResponseEntity<Student> createStudent(@RequestBody Student student) {
		Student std = studentRepository.save(student);
		return new ResponseEntity<>(std, HttpStatus.OK);
	}

	@PutMapping("/students/")
	public ResponseEntity<Student> updateStudent(@RequestBody Student student) {
		Optional<Student> std = studentRepository.findById(student.getId());
		Student stdUpdated = std.get();
		stdUpdated.setGrade(student.getGrade());
		stdUpdated.setName(student.getName());
		Student studentUpdated = studentRepository.save(stdUpdated);
		return new ResponseEntity<>(studentUpdated, HttpStatus.OK);
	}

	@DeleteMapping("/students/{id}")
	public ResponseEntity<String> createStudent(@PathVariable(name = "id") Long id) {
		studentRepository.deleteById(id);
		return new ResponseEntity<>("student id: "+ id + " deleted successfully", HttpStatus.OK);
	}
}
  • We have annotated the class with @RestController to make it a REST controller that contains our CRUD APIs. This annotation takes care of converting the response to JSON format, and handles the incoming requests, maps them to the proper handler methods.
  • @CrossOrigin: This annotation whitelists the domain origins that are allowed to request to the APIs.
  • Angular runs a development server at http://localhost:4200 when we execute the ng serve command. We have added the same URL is used with the @CrossOrigin annotation earlier.
  • We have injected the StudentRepository instance and added different methods to supports CRUD operation on the student entity.

We can run the application and test the endpoints.

Start the spring boot application, and our REST APIs will be available at http://localhost:8080/


Advertisements

Testing the APIs

We can use Postman or any other API testing tool to test the REST APIs.

Make sure that the Spring boot application is up. Now we can test the REST endpoints with the help of Postman.

Note: We may have to remove the @CrossOrigin annotation from the rest controller class, for now, to avoid CORS error.

Create API

Send the Post request with the student JSON request, as given below. The application will create a new student record in the H2 database.

save-student-jpa

Update API

Pass the student request JSON with id and update the name and grade fields as given below. The application will update the student details with new values.

student-update-jpa-springboot-angular

Read API

To retrieve all available students from the database, invoke the GET API, as given below.

student-get-api-spring-boot-angular

Delete API

Pass the student id in the request path and invoke the HTTP DELETE API, as given below.

The application will delete the student record from the H2 database that matches the request input id.

spring-boot-jpa-delete

Congratulations!! We have completed half part of our Spring Boot and Angular CRUD application.


Advertisements

Create Angular application

create a new angular application with the name springboot-angular-crud-app and navigate inside the application directory.

ng new springboot-angular-crud-app

cd springboot-angular-crud-app

Add bootstrap UI library to the angular application by running the below command.

ng add @ng-bootstrap/ng-bootstrap

We have added the NgBootstrap library to our angular application.

Create a new student component by running the below command.

ng generate component student

Update the app.componenet.html file with the below content.

<body>
  <header>
    <nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
      <div class="collapse navbar-collapse justify-content-center">
        <h4 class="text-light bg-dark">Student details</h4>
      </div>
    </nav>
  </header>

  <!-- Begin page content -->
  <main role="main" class="flex-shrink-0">
    <div class="container">
      <div class="mt-5 col-12 mt-6 d-flex justify-content-center">
        <app-student></app-student>
      </div>
    </div>
  </main>

  <footer class="footer fixed-bottom bg-dark">
    <div class="container">
      <span class="text-muted">ASB Notebook</span>
    </div>
  </footer>
</body>

We have added a navigation bar that contains a simple header with static text and a static footer.

The student component will get rendered within these two HTML components.

Run the below command within the application directory.

ng serve

Angular will compile the application, build it, and sets up a local server for running our application locally.

Our Angular application will be available at http://localhost:4200

angular-app-with-bootstrap-header-footer

Advertisements

Create service layer

Run the below command to generate a new Angular service.

ng generate service service/student

Angular service helps us create a utility class that we can use for various purposes, like making HTTP calls, connecting to local storage, etc.

Angular services class uses @Injectable decorator that makes it an angular service. We can inject the service class instances into any part of the application.

Replace the student.service.ts file with the below content.

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http'

let student_service = "http://localhost:8080/students/";

export interface StudentInterface {
  id : number;
  name : String;
  grade : number;
}

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

  constructor(private http : HttpClient) { }

  loadStudents() {
      return this.http.get<Array<StudentInterface>>(student_service);
  }

  createStudent(student: StudentInterface) {
    return this.http.post<StudentInterface>(student_service, student);
  }

  updateStudent(student: StudentInterface) {
    return this.http.put<StudentInterface>(student_service, student);
  }

  deleteStudent(id:number) {
    return this.http.delete<String>(student_service + id, { responseType: 'text' as 'json'}
    );
  }
}
  • We have imported HttpClient this will help us making HTTP connection with the Spring boot REST APIs.
  • We have created a StudentInterface type that holds values related to particular student entry.
  • The @Injectable decorator makes this class an angular service.
  • We have injected the HttpClient instance in the constructor method.
  • The loadStudents() method retrieves all available students list from the Spring boot application’s GET API.
  • The creatStudent(), updateStudent() methods calls POST and PUT APIs to create and update student details, respectively.
  • The deleteStudent() method invokes DELETE API and passes the student id in the API URL path.

Update the app.module.ts file and add the required imports.

//other imports
import { HttpClientModule } from '@angular/common/http';
import { StudentService } from './service/student.service';

@NgModule({
  imports: [
    HttpClientModule
  ],
  providers: [StudentService]
})
export class AppModule { }

The code snippet shows only the required changes to the existing file.

Update the student component

Update the student.component.ts file and replace the existing content with the below content.

import { Component, OnInit } from '@angular/core';
import { StudentService, StudentInterface } from './service/student.service';
import { NgbModal, NgbModalRef, NgbModalOptions } from '@ng-bootstrap/ng-bootstrap';

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

  _students: Array<StudentInterface> = [];
  _message: String = "";
  _student: StudentInterface;

  modalReference: NgbModalRef;
  modalOption: NgbModalOptions = {};

  constructor(private studentService: StudentService,
    private modalService: NgbModal) { }

  deleteStudent(id: number) {
    this.studentService.deleteStudent(id).subscribe(msg => this._message = msg);
    this._students.splice(this._students.findIndex(s => s.id === id), 1);
  }

  updateStudent(id: number) {
   this.studentService.updateStudent(this._student).subscribe(student => this._student = student);
  }

  addStudent() {
   this.studentService.createStudent(this._student).subscribe(student => {
      this._student = student;
      this._students.push(this._student);
    });
  }

  createUpdate() {
    if(this._student.id === null || this._student.id === 0){
      this.addStudent();
    } else {
      this.updateStudent(this._student.id);
    }
    this.modalReference.close();
  }

  ngOnInit(): void {
    this.studentService.loadStudents().subscribe(students => this._students = students);
    this._message = "";
  }

  open(id : number, content) {
    this.modalOption.backdrop = 'static';
    this.modalOption.keyboard = false;
    this.modalReference = this.modalService.open(content, this.modalOption);
    if(id === 0 ) {
      this._student = {id : 0, name : null, grade : 0};
    } else {
      this._student = this._students.find(s => s.id === id);
    }
  }
}
  • We have imported the StudentService, StudentInterface, that we have created earlier.
  • We have imported Bootstrap Modal related modules from the NgBootstrap library.
  • The StudentService and NgbModal instances are injected into the Angular component class using the class constructor.
  • The _students array will hold the list of students retrieved from the backend service.
  • The _message field holds the text message displayed on the deletion of a student record.
  • The _student instance holds the current student object that is being created/updated.
  • We have also defined bootstrap modal related instances to customize the default bootstrap modal.
  • The component class implements the OnInit component life cycle even, which loads all available students on Angular component initialization.
  • The open() method is invoked during the creation/update of new students. This method will open a bootstrap modal that contains input fields to enter student details.
  • The delete() method deletes the student instance and updates the message that gets displayed on the UI screen.

Update the app.module.ts file to add FormsModule, RectiveFormsModule, and add the required entry in the import section.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { StudentComponent } from './student/student.component';
import { HttpClientModule } from '@angular/common/http';
import { StudentService } from './service/student.service';

import { FormsModule, ReactiveFormsModule } from '@angular/forms';

@NgModule({
  declarations: [
    AppComponent,
    StudentComponent
  ],
  imports: [
    BrowserModule,
    NgbModule,
    HttpClientModule,
    FormsModule,
    ReactiveFormsModule
  ],
  providers: [StudentService],
  bootstrap: [AppComponent]
})
export class AppModule { }

The above code is the complete app.module.ts file of our application.

Update the student component template

Update the student.component.html file with the below content.

<div class="alert alert-danger" role="alert" *ngIf="_message.length > 0">
  {{_message}}
</div>
<table class="table table-striped">
  <thead>
    <tr>
      <th scope="col">Sl No.</th>
      <th scope="col">ID</th>
      <th scope="col">Name</th>
      <th scope="col">Grade</th>
      <th scope="col"></th>
      <th scope="col"><button class="btn btn-success" (click)="open(0, content)">ADD</button></th>
    </tr>
  </thead>
  <tbody>
    <tr *ngFor="let student of _students; index as i">
      <th scope="row">{{ i + 1 }}</th>
      <td>{{student.id}}</td>
      <td>{{ student.name }}</td>
      <td>{{ student.grade }}</td>
      <td><button class="btn btn-primary" (click)="open(student.id, content)">UPDATE</button></td>
      <td><button class="btn btn-danger" (click)="deleteStudent(student.id)">DELETE</button></td>
    </tr>
  </tbody>
</table>

<ng-template #content let-modal>
  <div class="modal-header">
    <h4 class="modal-title" id="modal-basic-title">Create/Update Student</h4>
    <button type="button" class="close"
      aria-label="Close" (click)="modal.dismiss('Cross click')" *ngIf="_student.id === 0">
      <span aria-hidden="true">×</span>
    </button>
  </div>
  <div class="modal-body">
    <form (ngSubmit)="createUpdate()">
      <div class="form-group">
        <h2>Name : </h2>
        <input type="text" name="name" placeholder="Student Name" [(ngModel)]="_student.name"/>
        <h2>Grade : </h2>
        <input type="number" name="grade" [(ngModel)]="_student.grade"/>
      </div>
      <button type="submit" class="btn btn-success">SAVE</button>
    </form>
</div>
  • The bootstrap alert will be displayed whenever a student record gets deleted.
  • All student records are shown in a table with UPDATE and DELETE buttons along with other details.
  • We have an ADD button that will open the bootstrap modal window to input student details.
  • The bootstrap window contains a SAVE button that invokes createUpdate() method to create/update the student details.

Congratulations! We have completed the front-end part of our Angular application.

Let us run and test it. 🙂


Advertisements

Testing the application

Run the application(if it is not running already :-)) using ng serve CLI command.

We should be able to get the below page.

angular-spring-boot-crud

Create a new student by clicking on the ADD button in the above screen.

This will open the bootstrap modal window as shown below.

angular-spring-boot-crud

Enter the name and grade input fields and click on the SAVE button. The application saves the student details by invoking the back-end Spring boot API and closes the modal window.

We can observe the saved student details in the below screen.

angular-spring-boot-crud

Update the student by clicking on the UPDATE button.

angular-spring-boot-crud

Delete the student record by clicking on the DELETE button.

angular-spring-boot-crud

Congratulations!! We have created a full-stack web application and performed basic CRUD operations. 🙂

Conclusion

In this article, we learned how to create a simple web application using Angular and Spring boot framework.

We created REST APIs to support CRUD operations.

We also learned how the Spring data JPA library provides built-in repository interfaces and provides data operation support.

We also learned how to create an Angular application, how to create an Angular service, and invoke the back end REST APIs.

We also learned how to add bootstrap support to our Angular application.

The complete example is available on GitHub.

You may also be interested in