Spring Boot 3 + MongoDB + Angular 15 CRUD Application Example
Hello everyone, today we will learn how to develop a full-stack web application that is a basic User Management Application using Spring Boot 3, MongoDB, and Angular 15. You could download the source code from our Github repository, the download link is provided at the end of this tutorial.
After completing this tutorial what we will build?
We will build a full-stack web application that is a basic User Management Application with CRUD features:
• Create User
• List User
• Update User
• Delete User
We divided this tutorial into two parts.
PART 1 - Restful API Development with Spring Boot 3 & MongoDB.
PART 2 - UI development using Angular 15.
PART 1 - Restful API Development with Spring Boot 3
These are APIs that Spring backend App will export:
Backend project structure:
Maven[pom.xml]
A Project Object Model or POM is the fundamental unit of work in Maven. It is an XML file that contains information about the project and configuration details utilized by Maven to build the project.
<?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>3.0.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.knf.dev.demo</groupId>
<artifactId>crud-application</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>crud-application</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Configuring MongoDB Database
Go to your MongoDB Compass and create a Database named userdb and inside the database create a collection named user as seen in the below image.
Open src/main/resources/application.properties file and add the following content to it:
spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017
spring.data.mongodb.database=userdb
Create User Document
package com.knf.dev.demo.crudapplication.model;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Document(collection = "user")
public class User {
@Id
private String id;
private String firstName;
private String lastName;
private String emailId;
public User() {
}
public User(String firstName,
String lastName, String emailId) {
super();
this.firstName = firstName;
this.lastName = lastName;
this.emailId = emailId;
}
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;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
Create UserRepository
package com.knf.dev.demo.crudapplication.repository;
import com.knf.dev.demo.crudapplication.model.User;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends MongoRepository<User, String> {
}
Create CustomErrorResponse
package com.knf.dev.demo.crudapplication.exception;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.time.LocalDateTime;
public class CustomErrorResponse {
@JsonFormat(shape = JsonFormat.Shape.STRING,
pattern = "yyyy-MM-dd hh:mm:ss")
private LocalDateTime timestamp;
private int status;
private String error;
public LocalDateTime getTimestamp() {
return timestamp;
}
public void setTimestamp(LocalDateTime timestamp) {
this.timestamp = timestamp;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
}
Create ResourceNotFoundException
package com.knf.dev.demo.crudapplication.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);
}
}
Create GlobalExceptionHandler
package com.knf.dev.demo.crudapplication.exception;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import java.time.LocalDateTime;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<CustomErrorResponse> globalExceptionHandler
(Exception ex, WebRequest request) {
CustomErrorResponse errors = new CustomErrorResponse();
errors.setTimestamp(LocalDateTime.now());
errors.setError(ex.getMessage());
errors.setStatus(HttpStatus.NOT_FOUND.value());
return new ResponseEntity<>(errors, HttpStatus.NOT_FOUND);
}
}
Create UserController
package com.knf.dev.demo.crudapplication.controller;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.knf.dev.demo.crudapplication.model.User;
import com.knf.dev.demo.crudapplication.exception.ResourceNotFoundException;
import com.knf.dev.demo.crudapplication.repository.UserRepository;
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;
@CrossOrigin(origins = "*")
@RestController
@RequestMapping("/api/v1")
public class UserController {
@Autowired
private UserRepository userRepository;
// get all users
@GetMapping("/users")
public List<User> getAllUsers() {
return userRepository.findAll();
}
// create user rest API
@PostMapping("/users")
public User createUser(@RequestBody User user) {
SecureRandom random = new SecureRandom();
user.setId(user.getEmailId()+"@"+random.nextInt(1000));
return userRepository.save(user);
}
// get user by id rest api
@GetMapping("/users/{id}")
public ResponseEntity<User> getUserById(@PathVariable String id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException
("User not exist with id :" + id));
return ResponseEntity.ok(user);
}
// update user rest api
@PutMapping("/users/{id}")
public ResponseEntity<User> updateUser(@PathVariable String id,
@RequestBody User userDetails) {
User user = userRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException
("User not exist with id :" + id));
user.setFirstName(userDetails.getFirstName());
user.setLastName(userDetails.getLastName());
user.setEmailId(userDetails.getEmailId());
User updatedUser = userRepository.save(user);
return ResponseEntity.ok(updatedUser);
}
// delete user rest api
@DeleteMapping("/users/{id}")
public ResponseEntity<Map<String, Boolean>> deleteUser
(@PathVariable String id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException
("User not exist with id :" + id));
userRepository.delete(user);
Map<String, Boolean> response = new HashMap<>();
response.put("deleted", Boolean.TRUE);
return ResponseEntity.ok(response);
}
}
Main Driver
package com.knf.dev.demo.crudapplication;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class CrudApplication {
public static void main(String[] args) {
SpringApplication.run(CrudApplication.class, args);
}
}
Run the application and verify REST APIs
Step 1: mvn clean install
Step 2: Run the Spring Boot application - mvn spring-boot:run
Add User:
Fetch all Users:
Get User by ID:
Delete User by ID:
PART 2 - UI development using Angular 15
Project Structure
The package.json
A package.json is a JSON file that subsists at the root of a Javascript/Node project. It holds metadata pertinent to the project and is utilized for managing the project's dependencies, scripts, version, and a whole lot more.{ "name": "crud-application", "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": "^15.0.0", "@angular/common": "^15.0.0", "@angular/compiler": "^15.0.0", "@angular/core": "^15.0.0", "@angular/forms": "^15.0.0", "@angular/platform-browser": "^15.0.0", "@angular/platform-browser-dynamic": "^15.0.0", "@angular/router": "^15.0.0", "bootstrap": "^4.6.2", "rxjs": "~7.5.0", "tslib": "^2.3.0", "zone.js": "~0.12.0" }, "devDependencies": { "@angular-devkit/build-angular": "^15.0.3", "@angular/cli": "~15.0.3", "@angular/compiler-cli": "^15.0.0", "@types/jasmine": "~4.3.0", "jasmine-core": "~4.5.0", "karma": "~6.4.0", "karma-chrome-launcher": "~3.1.0", "karma-coverage": "~2.2.0", "karma-jasmine": "~5.1.0", "karma-jasmine-html-reporter": "~2.0.0", "typescript": "~4.8.2" }}
A package.json is a JSON file that subsists at the root of a Javascript/Node project. It holds metadata pertinent to the project and is utilized for managing the project's dependencies, scripts, version, and a whole lot more.
{ "name": "crud-application", "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": "^15.0.0", "@angular/common": "^15.0.0", "@angular/compiler": "^15.0.0", "@angular/core": "^15.0.0", "@angular/forms": "^15.0.0", "@angular/platform-browser": "^15.0.0", "@angular/platform-browser-dynamic": "^15.0.0", "@angular/router": "^15.0.0", "bootstrap": "^4.6.2", "rxjs": "~7.5.0", "tslib": "^2.3.0", "zone.js": "~0.12.0" }, "devDependencies": { "@angular-devkit/build-angular": "^15.0.3", "@angular/cli": "~15.0.3", "@angular/compiler-cli": "^15.0.0", "@types/jasmine": "~4.3.0", "jasmine-core": "~4.5.0", "karma": "~6.4.0", "karma-chrome-launcher": "~3.1.0", "karma-coverage": "~2.2.0", "karma-jasmine": "~5.1.0", "karma-jasmine-html-reporter": "~2.0.0", "typescript": "~4.8.2" }}
The User.ts file(User Model)
Path - src/app/service/user.ts
export class User { id!: string; firstName!: String; lastName!: String; emailId!: String;}
export class User { id!: string; firstName!: String; lastName!: String; emailId!: String;}
CRUD Service
Path - src/app/service/crud.service.ts
The CrudService will be used to get the data from the backend by calling APIs. Update the crud.service.ts file inside src/app/service directory with the following code to it -import { Injectable } from '@angular/core';import { User } from './User';import { catchError, map } from 'rxjs/operators';import { Observable, throwError } from 'rxjs';import { HttpClient, HttpHeaders, HttpErrorResponse,} from '@angular/common/http';
@Injectable({ providedIn: 'root',})export class CrudService { REST_API: string = 'http://localhost:8080/api/v1';
// Http Header httpHeaders = new HttpHeaders().set('Content-Type', 'application/json');
constructor(private httpClient: HttpClient) {}
// Add AddUser(data: User): Observable<any> { let API_URL = `${this.REST_API}/users`; return this.httpClient .post(API_URL, data) .pipe(catchError(this.handleError)); }
// Get all objects GetUsers() { return this.httpClient.get(`${this.REST_API}/users`); }
// Get single object GetUser(id: any): Observable<any> { let API_URL = `${this.REST_API}/users/${id}`; return this.httpClient.get(API_URL, { headers: this.httpHeaders }).pipe( map((res: any) => { return res || {}; }), catchError(this.handleError) ); }
// Update updateUser(id: any, data: any): Observable<any> { let API_URL = `${this.REST_API}/users/${id}`; return this.httpClient .put(API_URL, data, { headers: this.httpHeaders }) .pipe(catchError(this.handleError)); }
// Delete deleteUser(id: any): Observable<any> { let API_URL = `${this.REST_API}/users/${id}`; return this.httpClient .delete(API_URL, { headers: this.httpHeaders }) .pipe(catchError(this.handleError)); }
// Error handleError(error: HttpErrorResponse) { let errorMessage = ''; if (error.error instanceof ErrorEvent) { // Handle client error errorMessage = error.error.message; } else { // Handle server error errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`; } console.log(errorMessage); return throwError(() => { errorMessage; }); }}
Path - src/app/service/crud.service.ts
The CrudService will be used to get the data from the backend by calling APIs. Update the crud.service.ts file inside src/app/service directory with the following code to it -
import { Injectable } from '@angular/core';import { User } from './User';import { catchError, map } from 'rxjs/operators';import { Observable, throwError } from 'rxjs';import { HttpClient, HttpHeaders, HttpErrorResponse,} from '@angular/common/http';
@Injectable({ providedIn: 'root',})export class CrudService { REST_API: string = 'http://localhost:8080/api/v1';
// Http Header httpHeaders = new HttpHeaders().set('Content-Type', 'application/json');
constructor(private httpClient: HttpClient) {}
// Add AddUser(data: User): Observable<any> { let API_URL = `${this.REST_API}/users`; return this.httpClient .post(API_URL, data) .pipe(catchError(this.handleError)); }
// Get all objects GetUsers() { return this.httpClient.get(`${this.REST_API}/users`); }
// Get single object GetUser(id: any): Observable<any> { let API_URL = `${this.REST_API}/users/${id}`; return this.httpClient.get(API_URL, { headers: this.httpHeaders }).pipe( map((res: any) => { return res || {}; }), catchError(this.handleError) ); }
// Update updateUser(id: any, data: any): Observable<any> { let API_URL = `${this.REST_API}/users/${id}`; return this.httpClient .put(API_URL, data, { headers: this.httpHeaders }) .pipe(catchError(this.handleError)); }
// Delete deleteUser(id: any): Observable<any> { let API_URL = `${this.REST_API}/users/${id}`; return this.httpClient .delete(API_URL, { headers: this.httpHeaders }) .pipe(catchError(this.handleError)); }
// Error handleError(error: HttpErrorResponse) { let errorMessage = ''; if (error.error instanceof ErrorEvent) { // Handle client error errorMessage = error.error.message; } else { // Handle server error errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`; } console.log(errorMessage); return throwError(() => { errorMessage; }); }}
User List Component
Let's create the UserListComponent component which will be used to display a list of users, create a new user, and delete a user.
Path - src/app/user-list/user-list.component.tsimport { Component, OnInit } from '@angular/core';import { CrudService } from '../../service/crud.service';
@Component({ selector: 'app-users-list', templateUrl: './users-list.component.html', styleUrls: ['./users-list.component.scss'],})export class UsersListComponent implements OnInit { Users: any = [];
constructor(private crudService: CrudService) {}
ngOnInit(): void { this.crudService.GetUsers().subscribe((res: any) => { console.log(res); this.Users = res; }); }
delete(id: any, i: any) { console.log(id); if (window.confirm('Do you want to go ahead?')) { this.crudService.deleteUser(id).subscribe(() => { this.Users.splice(i, 1); }); } }}
Let's create the UserListComponent component which will be used to display a list of users, create a new user, and delete a user.
Path - src/app/user-list/user-list.component.ts
import { Component, OnInit } from '@angular/core';import { CrudService } from '../../service/crud.service';
@Component({ selector: 'app-users-list', templateUrl: './users-list.component.html', styleUrls: ['./users-list.component.scss'],})export class UsersListComponent implements OnInit { Users: any = [];
constructor(private crudService: CrudService) {}
ngOnInit(): void { this.crudService.GetUsers().subscribe((res: any) => { console.log(res); this.Users = res; }); }
delete(id: any, i: any) { console.log(id); if (window.confirm('Do you want to go ahead?')) { this.crudService.deleteUser(id).subscribe(() => { this.Users.splice(i, 1); }); } }}
User List Template
Path - src/app/user-list/user-list.component.html
Add user-list.component.html file with the following code to it -<div class="container"> <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom" > <h2 class="h2">Users Collection</h2> </div>
<div class="table-responsive"> <table class="table table-bordered"> <thead> <tr> <th scope="col">First Name</th> <th scope="col">Last Name</th> <th scope="col">Email</th> <th class="text-center" scope="col">Action</th> </tr> </thead> <tbody> <tr *ngFor="let user of Users; let i = index"> <td>{{ user.firstName }}</td> <td>{{ user.lastName }}</td> <td>{{ user.emailId }}</td> <td class="text-center"> <button class="btn btn-sm btn-primary" routerLink="/edit-user/{{ user.id }}" > Edit </button> <button class="btn btn-sm btn-danger" (click)="delete(user.id, i)"> Delete </button> </td> </tr> </tbody> </table> </div></div>
Path - src/app/user-list/user-list.component.html
Add user-list.component.html file with the following code to it -
<div class="container"> <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom" > <h2 class="h2">Users Collection</h2> </div>
<div class="table-responsive"> <table class="table table-bordered"> <thead> <tr> <th scope="col">First Name</th> <th scope="col">Last Name</th> <th scope="col">Email</th> <th class="text-center" scope="col">Action</th> </tr> </thead> <tbody> <tr *ngFor="let user of Users; let i = index"> <td>{{ user.firstName }}</td> <td>{{ user.lastName }}</td> <td>{{ user.emailId }}</td> <td class="text-center"> <button class="btn btn-sm btn-primary" routerLink="/edit-user/{{ user.id }}" > Edit </button> <button class="btn btn-sm btn-danger" (click)="delete(user.id, i)"> Delete </button> </td> </tr> </tbody> </table> </div></div>
Add User Component
Path - src/app/add-user/add-user.component.ts
AddUserComponent is used to create and handle new user form data. Add the following code to it -import { Component, OnInit, NgZone } from '@angular/core';import { Router } from '@angular/router';import { CrudService } from '../../service/crud.service';import { FormGroup, FormBuilder } from '@angular/forms';
@Component({ selector: 'app-add-user', templateUrl: './add-user.component.html', styleUrls: ['./add-user.component.scss'],})export class AddUserComponent implements OnInit { userForm: FormGroup;
constructor( public formBuilder: FormBuilder, private router: Router, private ngZone: NgZone, private crudService: CrudService ) { this.userForm = this.formBuilder.group({ firstName: [''], lastName: [''], emailId: [''], }); }
ngOnInit() {}
onSubmit(): any { this.crudService.AddUser(this.userForm.value).subscribe( () => { console.log('Data added successfully!'); this.ngZone.run(() => this.router.navigateByUrl('/users-list')); }, (err) => { console.log(err); } ); }}
Path - src/app/add-user/add-user.component.ts
AddUserComponent is used to create and handle new user form data. Add the following code to it -
import { Component, OnInit, NgZone } from '@angular/core';import { Router } from '@angular/router';import { CrudService } from '../../service/crud.service';import { FormGroup, FormBuilder } from '@angular/forms';
@Component({ selector: 'app-add-user', templateUrl: './add-user.component.html', styleUrls: ['./add-user.component.scss'],})export class AddUserComponent implements OnInit { userForm: FormGroup;
constructor( public formBuilder: FormBuilder, private router: Router, private ngZone: NgZone, private crudService: CrudService ) { this.userForm = this.formBuilder.group({ firstName: [''], lastName: [''], emailId: [''], }); }
ngOnInit() {}
onSubmit(): any { this.crudService.AddUser(this.userForm.value).subscribe( () => { console.log('Data added successfully!'); this.ngZone.run(() => this.router.navigateByUrl('/users-list')); }, (err) => { console.log(err); } ); }}
Add User Template
The add-user.component.html shows the add using HTML form. Add the following code to it -
Path - src/app/add-user/add-user.component.html <div class="row justify-content-center mt-5"> <div class="col-md-4"> <form [formGroup]="userForm" (ngSubmit)="onSubmit()"> <div class="form-group"> <label>First Name</label> <input class="form-control" type="text" formControlName="firstName" required /> </div>
<div class="form-group"> <label>Last Name</label> <input class="form-control" type="text" formControlName="lastName" required /> </div>
<div class="form-group"> <label>Email</label> <input class="form-control" type="text" formControlName="emailId" required /> </div> <br> <div class="form-group"> <button class="btn btn-primary btn-block" type="submit"> Add User </button> </div> </form> </div></div>
The add-user.component.html shows the add using HTML form. Add the following code to it -
Path - src/app/add-user/add-user.component.html
<div class="row justify-content-center mt-5"> <div class="col-md-4"> <form [formGroup]="userForm" (ngSubmit)="onSubmit()"> <div class="form-group"> <label>First Name</label> <input class="form-control" type="text" formControlName="firstName" required /> </div>
<div class="form-group"> <label>Last Name</label> <input class="form-control" type="text" formControlName="lastName" required /> </div>
<div class="form-group"> <label>Email</label> <input class="form-control" type="text" formControlName="emailId" required /> </div> <br> <div class="form-group"> <button class="btn btn-primary btn-block" type="submit"> Add User </button> </div> </form> </div></div>
User Detail Component
Path - src/app/user-detail/user-detail.component.ts
The UserDetailComponent component is used to display a particular user detail. Add the following code to it -import { Component, OnInit, NgZone } from '@angular/core';import { Router, ActivatedRoute } from '@angular/router';import { CrudService } from '../../service/crud.service';import { FormGroup, FormBuilder } from '@angular/forms';
@Component({ selector: 'app-user-detail', templateUrl: './user-detail.component.html', styleUrls: ['./user-detail.component.scss'],})export class UserDetailComponent implements OnInit { getId: any; updateForm: FormGroup;
constructor( public formBuilder: FormBuilder, private router: Router, private ngZone: NgZone, private activatedRoute: ActivatedRoute, private crudService: CrudService ) { this.getId = this.activatedRoute.snapshot.paramMap.get('id');
this.crudService.GetUser(this.getId).subscribe((res: { [x: string]: any; }) => { this.updateForm.setValue({ firstName: res['firstName'], lastName: res['lastName'], emailId: res['emailId'], }); });
this.updateForm = this.formBuilder.group({ firstName: [''], lastName: [''], emailId: [''], }); }
ngOnInit() {}
onUpdate(): any { this.crudService.updateUser(this.getId, this.updateForm.value).subscribe( () => { console.log('Data updated successfully!'); this.ngZone.run(() => this.router.navigateByUrl('/users-list')); }, (err) => { console.log(err); } ); }}
Path - src/app/user-detail/user-detail.component.ts
The UserDetailComponent component is used to display a particular user detail. Add the following code to it -
import { Component, OnInit, NgZone } from '@angular/core';import { Router, ActivatedRoute } from '@angular/router';import { CrudService } from '../../service/crud.service';import { FormGroup, FormBuilder } from '@angular/forms';
@Component({ selector: 'app-user-detail', templateUrl: './user-detail.component.html', styleUrls: ['./user-detail.component.scss'],})export class UserDetailComponent implements OnInit { getId: any; updateForm: FormGroup;
constructor( public formBuilder: FormBuilder, private router: Router, private ngZone: NgZone, private activatedRoute: ActivatedRoute, private crudService: CrudService ) { this.getId = this.activatedRoute.snapshot.paramMap.get('id');
this.crudService.GetUser(this.getId).subscribe((res: { [x: string]: any; }) => { this.updateForm.setValue({ firstName: res['firstName'], lastName: res['lastName'], emailId: res['emailId'], }); });
this.updateForm = this.formBuilder.group({ firstName: [''], lastName: [''], emailId: [''], }); }
ngOnInit() {}
onUpdate(): any { this.crudService.updateUser(this.getId, this.updateForm.value).subscribe( () => { console.log('Data updated successfully!'); this.ngZone.run(() => this.router.navigateByUrl('/users-list')); }, (err) => { console.log(err); } ); }}
User Details Template
Path - src/app/user-detail/user-detail.component.html
The user-detail.component.html displays a particular user detail. Add the following code to it -<div class="row justify-content-center mt-5"> <div class="col-md-4"> <form [formGroup]="updateForm" (ngSubmit)="onUpdate()"> <div class="form-group"> <label>First Name</label> <input class="form-control" type="text" formControlName="firstName" required /> </div>
<div class="form-group"> <label>Last Name</label> <input class="form-control" type="text" formControlName="lastName" required /> </div>
<div class="form-group"> <label>Email</label> <input class="form-control" type="text" formControlName="emailId" required /> </div> <br> <div class="form-group"> <button class="btn btn-primary btn-block" type="submit">Update</button> </div> </form> </div></div>
Path - src/app/user-detail/user-detail.component.html
The user-detail.component.html displays a particular user detail. Add the following code to it -
<div class="row justify-content-center mt-5"> <div class="col-md-4"> <form [formGroup]="updateForm" (ngSubmit)="onUpdate()"> <div class="form-group"> <label>First Name</label> <input class="form-control" type="text" formControlName="firstName" required /> </div>
<div class="form-group"> <label>Last Name</label> <input class="form-control" type="text" formControlName="lastName" required /> </div>
<div class="form-group"> <label>Email</label> <input class="form-control" type="text" formControlName="emailId" required /> </div> <br> <div class="form-group"> <button class="btn btn-primary btn-block" type="submit">Update</button> </div> </form> </div></div>
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 { UsersListComponent } from './components/users-list/users-list.component';import { AddUserComponent } from './components/add-user/add-user.component';import { UserDetailComponent } from './components/user-detail/user-detail.component';
const routes: Routes = [ { path: '', pathMatch: 'full', redirectTo: 'add-user' }, { path: 'users-list', component: UsersListComponent }, { path: 'add-user', component: AddUserComponent }, { path: 'edit-user/:id', component: UserDetailComponent },];
@NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule],})export class AppRoutingModule {}
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 { UsersListComponent } from './components/users-list/users-list.component';import { AddUserComponent } from './components/add-user/add-user.component';import { UserDetailComponent } from './components/user-detail/user-detail.component';
const routes: Routes = [ { path: '', pathMatch: 'full', redirectTo: 'add-user' }, { path: 'users-list', component: UsersListComponent }, { path: 'add-user', component: AddUserComponent }, { path: 'edit-user/:id', component: UserDetailComponent },];
@NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule],})export class AppRoutingModule {}
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. import { BrowserModule } from '@angular/platform-browser';import { NgModule } from '@angular/core';import { AppComponent } from './app.component';import { FormsModule, ReactiveFormsModule } from '@angular/forms';import { HttpClientModule } from '@angular/common/http';import { AppRoutingModule } from './app-routing.module';import { AddUserComponent } from './components/add-user/add-user.component';import { UserDetailComponent } from './components/user-detail/user-detail.component';import { UsersListComponent } from './components/users-list/users-list.component';
@NgModule({ declarations: [ AppComponent, AddUserComponent, UserDetailComponent, UsersListComponent, ], imports: [ BrowserModule, HttpClientModule, FormsModule, ReactiveFormsModule, AppRoutingModule, ], providers: [], bootstrap: [AppComponent],})export class AppModule {}
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.
import { BrowserModule } from '@angular/platform-browser';import { NgModule } from '@angular/core';import { AppComponent } from './app.component';import { FormsModule, ReactiveFormsModule } from '@angular/forms';import { HttpClientModule } from '@angular/common/http';import { AppRoutingModule } from './app-routing.module';import { AddUserComponent } from './components/add-user/add-user.component';import { UserDetailComponent } from './components/user-detail/user-detail.component';import { UsersListComponent } from './components/users-list/users-list.component';
@NgModule({ declarations: [ AppComponent, AddUserComponent, UserDetailComponent, UsersListComponent, ], imports: [ BrowserModule, HttpClientModule, FormsModule, ReactiveFormsModule, AppRoutingModule, ], providers: [], bootstrap: [AppComponent],})export class AppModule {}
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.scss']})export class AppComponent { title = 'Spring Boot 3 + MongoDB + Angular 15 CRUD Application';}
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.scss']})export class AppComponent { title = 'Spring Boot 3 + MongoDB + Angular 15 CRUD Application';}
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-success navbar-dark"> <!-- Links --> <ul class="navbar-nav"> <li class="nav-item"> <a class="nav-link" routerLinkActive="active" routerLink="/users-list" >Show Users</a> </li> <li class="nav-item"> <a class="nav-link" routerLinkActive="active" routerLink="/add-user" >Add Users</a > </li> </ul></nav><div class="container"> <br> <h2 style="text-align: center;">{{title}}</h2> <hr> <router-outlet></router-outlet></div>
<router-outlet></router-outlet>
Path: /src/app/app.component.html
Defines the HTML template associated with the root AppComponent.
<nav class="navbar navbar-expand-sm bg-success navbar-dark"> <!-- Links --> <ul class="navbar-nav"> <li class="nav-item"> <a class="nav-link" routerLinkActive="active" routerLink="/users-list" >Show Users</a> </li> <li class="nav-item"> <a class="nav-link" routerLinkActive="active" routerLink="/add-user" >Add Users</a > </li> </ul></nav><div class="container"> <br> <h2 style="text-align: center;">{{title}}</h2> <hr> <router-outlet></router-outlet></div>
<router-outlet></router-outlet>
index.html
Path: /src/index.html
The main index.html file is the initial page loaded by the browser that kicks everything off.<!doctype html><html lang="en"><head> <meta charset="utf-8"> <title>AngularSpringMongoDBCrud</title> <base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="favicon.ico"></head><body> <app-root></app-root></body></html>
Path: /src/index.html
The main index.html file is the initial page loaded by the browser that kicks everything off.
<!doctype html><html lang="en"><head> <meta charset="utf-8"> <title>AngularSpringMongoDBCrud</title> <base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="favicon.ico"></head><body> <app-root></app-root></body></html>
Main/Bootstrap File
Path: /src/main.ts
The main file is the entry point used by angular to launch and bootstrap the application.import { enableProdMode } from '@angular/core';import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';import { environment } from './environments/environment';
if (environment.production) { enableProdMode();}
platformBrowserDynamic().bootstrapModule(AppModule) .catch((err: any) => console.error(err));
Path: /src/main.ts
The main file is the entry point used by angular to launch and bootstrap the application.
import { enableProdMode } from '@angular/core';import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';import { environment } from './environments/environment';
if (environment.production) { enableProdMode();}
platformBrowserDynamic().bootstrapModule(AppModule) .catch((err: any) => console.error(err));
Local Setup and Run the application
Step 1: Install Mongo DB - Click here
Step 2: Download or clone the source code from GitHub to a local machine - Click here
Backend
Step 3: mvn clean install
Step 4: Run the Spring Boot application - mvn spring-boot:run
Frontend
Step 5: npm install
Step 6: ng serve