Building a Full-Stack Web Application with Angular 15 and Spring Boot 3 from SCRATCH (2023)
This tutorial covers:
Introduction
In today's interconnected world, building robust and scalable web applications is essential for businesses to thrive. In this article, we'll explore the process of creating a full-stack web application using Angular and Spring Boot. We'll cover the steps involved, from setting up the development environment to implementing the frontend interface and backend API. So, let's dive in!
In this tutorial, we are going to share how to build a Full-Stack Web Application with Angular 15 and Spring Boot 3 using MongoDB? NoSQL database.
This tutorial helps you cover the following topics:
Global Architecture of the project
1- Micro services Architecture
Microservices refer to both an architecture and a software development approach that involves breaking down applications into smaller, independent components. Unlike traditional monolithic approaches, where all components are tightly coupled as a single entity, microservices focus on creating loosely coupled and independently deployable services.
2- Logical architecture
Our application follows a logical architecture composed of three layers:
By dividing the application into these three layers, we achieve separation of concerns and maintain a clear separation between user interface, data manipulation, and business rules. This architectural pattern enhances modularity, scalability, and maintainability of the application, as each layer can be developed, tested, and modified independently, facilitating code reuse and improving overall system performance and flexibility.
Creating a sample Angular application
Step 1 : Installing the Angular CLI
The Angular CLI is a command-line interface tool that you use to initialize, develop, scaffold, and maintain Angular applications directly from a command shell.
In order to create an Angular project, it is necessary to install the Angular CLI on the development system. This requires the installation of Node.js.
Follow the Node.js installation URL on your developer environment.
Install the CLI using the npm package manager on a terminal:
npm install -g @angular/cli
Step 2 : Creating an Angular Project
To get started, let's create a fresh Angular project using Angular CLI by typing the command below (on a terminal) with the project name “angular-springboot-app”.
ng new angular-springboot-app
Hit y to a dd Angular Routing to your project
Hit Enter to choose stylesheet format css (Cascading Style Sheets)
Once the Angular project is setup then get into the project folder by using the following command.
cd angular-springboot-app
Step 3 : Open Angular Project
Your basic Angular app is almost ready just hit the below command in terminal.
ng serve --open
Now your browser should be opened and you'll see this
Now everything should be fine. Let's jump to the back-end.
Create a Spring Boot application
Step 1 : Installing the JRE, SpringTools
To start developing Spring Boot project, It is important to ensure that you have Java installed on your system. If you haven't already installed Java, you can follow the Java installation URL
Once Java is successfully installed, the next step is to set up an Integrated Development Environment (IDE) to work with Spring Boot. One popular choice is the Spring Tools Suite (STS). You can download and install STS by visiting the Spring Tools website.
By installing Java and then configuring STS, you will have a robust development environment ready to create an amazing Spring Boot application. So, let's get to building our project.
Step 2 : Creating an Springboot Project
To get started, let's create a fresh Springboot project using spring initializr by following the provided information in the picture below
Customize your project as per your requirements, but it is recommended to keep the Java version in your Spring Boot project equal to or lower than the version installed on your computer.
Next add these Depandencies :
Once you have completed setting up your Spring Boot project, it's time to generate the project by clicking the "Generate" button, as shown in the picture below
Congratulations! You now have a fully functional Spring Boot project downloaded to your computer.
Step 3: Installing MongoDB (DataBase)
To integrate MongoDB as our NoSQL database in our Spring Boot application, we need to install MongoDB Compass on our system. Follow these steps to install MongoDB:
Once you have completed the installation of MongoDB, you can proceed to launch MongoDB Compass, a graphical user interface (GUI) tool, to manage and interact with your MongoDB server. MongoDB Compass provides a user-friendly interface for performing various database operations and querying data.
By using MongoDB Compass, you can perform a variety of tasks, such as creating databases, managing collections, inserting and updating documents, executing queries, and more. The intuitive GUI provided by MongoDB Compass simplifies the process of interacting with your MongoDB server.
Now that you have MongoDB Compass up and running, you are ready to configure your Spring Boot application to connect to MongoDB and leverage its powerful features for data storage and retrieval.
If you find these concepts overwhelming or confusing, don't worry! Now it's time to dive into the exciting part: coding.
Building Backend(SpringBoot Project)
Step 1: upload project in IDE
To start building the backend of your web application using Spring Boot, you'll need to upload the project to your Integrated Development Environment (IDE). Follow these steps to import the project into your IDE:
Step 2: Creating Packages
After uploading your Spring Boot project to your IDE, the next step is to organize your project structure by creating packages. Packages provide a logical grouping of related classes and components within your project. Follow these steps to create packages in your Spring Boot project
We will create 4 Packages divied like this :
Step 3: Creating Entities
Entities represent the data models of your application and define the structure and relationships between the data objects. In this step, we will create the entity classes within the "entities" package.
For the purpose of this tutorial, we will demonstrate a simple example using a single case of a relationship between classes. If you're interested in exploring more complex relationships, such as one-to-many, many-to-one, or many-to-many, you can refer to the provided link for detailed information and examples.
Follow these steps to create the entities in your Spring Boot application:
Role Entity
package com.linkedin.demo.springboot.entities;
import java.io.Serializable;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Document(collection = "Roles")
public class Role implements Serializable{
@Id
private String id;
private String roleName;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
}
User Entity
package com.linkedin.demo.springboot.entities;
import java.io.Serializable;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;
@Document(collection = "Users")
public class User implements Serializable {
@Id
private String id;
private String userName;
private String lastName;
@DBRef
? ? private Role role;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
}
Step 4: Creating Repositories
Repositories in Spring Boot provide a convenient way to interact with the database and perform CRUD (Create, Read, Update, Delete) operations on your entities. In this step, we will create repository interfaces within the "repositories" package of our Spring Boot project.
Role Repository
package com.linkedin.demo.springboot.repositories;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
import com.linkedin.demo.springboot.entities.Role;
@Repository
public interface RoleRepository extends MongoRepository<Role,String>{}
User Repository
package com.linkedin.demo.springboot.repositories;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
import com.linkedin.demo.springboot.entities.User;
@Repository
public interface UserRepository extends MongoRepository<User,String>{}
The @Repository annotation is used in Spring Framework to indicate that a class is a repository or a data access object (DAO). It is typically applied to classes that perform database operations, such as querying, inserting, updating, or deleting data.
Step 5: Creating Services(CRUD)
Services in a Spring Boot application handle the business logic and act as an intermediary between the controllers and repositories. In this step, we will create service classes within the "services" package of your Spring Boot project to perform CRUD (Create, Read, Update, Delete) operations on the entities.
Role Service
package com.linkedin.demo.springboot.services;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.linkedin.demo.springboot.entities.Role;
import com.linkedin.demo.springboot.repositories.RoleRepository;
@Service
public class RoleService {
@Autowired
RoleRepository roleRepository;
// function to create a new role
public Role createRole(Role role) {
? ? ? ? return roleRepository.save(role);
? ? }
// function return a role by id or null in case it doesn't exist
? ? public Role getRoleById(String id) {
? ? ? ? return roleRepository.findById(id).orElse(null);
? ? }
? ? // function that retrieve all roles from database
? ? public List<Role> getAllRoles() {
? ? ? ? return roleRepository.findAll();
? ? }
? ? // function that update an existing role in database
? ? public Role updateRole(Role role) {
? ? ? ? return roleRepository.save(role);
? ? }
? ? // function that remove a role from database by it's id
? ? public void deleteRole(String id) {
? ? roleRepository.deleteById(id);
? ? }
}
User Service
package com.linkedin.demo.springboot.services;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.linkedin.demo.springboot.entities.User;
import com.linkedin.demo.springboot.repositories.UserRepository;
@Service
public class UserService {
@Autowired
UserRepository userRepository;
// function to create a new user
public User createUser(User user) {
? ? ? ? return userRepository.save(user);
? ? }
// function return a user by id or null in case it doesn't exist
? ? public User getUserById(String id) {
? ? ? ? return userRepository.findById(id).orElse(null);
? ? }
? ? // function that retrieve all users from database
? ? public List<User> getAllUsers() {
? ? ? ? return userRepository.findAll();
? ? }
? ? // function that update an existing user in database
? ? public User updateUser(User user) {
? ? ? ? return userRepository.save(user);
? ? }
? ? // function that remove a user from database by it's id
? ? public void deleteUser(String id) {
? ? ? ? userRepository.deleteById(id);
? ? }
}
Step 6: Creating Controllers
In this step, we will create controllers in our Spring Boot project. Controllers are responsible for handling incoming HTTP requests, processing them, and returning appropriate responses.
User Controller
package com.linkedin.demo.springboot.controllers;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.linkedin.demo.springboot.entities.User;
import com.linkedin.demo.springboot.services.UserService;
@CrossOrigin(origins="*")
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
UserService userService;
//Create new User
@PostMapping(consumes =MediaType.APPLICATION_JSON_VALUE,produces =MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<User> createUser(@RequestBody User user){
User newUser = userService.createUser(user);
return new ResponseEntity<User>(newUser,HttpStatus.CREATED);
}
//Get user by id
? ? @GetMapping(path="/{userId}",produces =MediaType.APPLICATION_JSON_VALUE)
? ? public ResponseEntity<User> getUserById(@PathVariable String userId) {
? ? User userFound = userService.getUserById(userId);
? ? return new ResponseEntity<User>(userFound,HttpStatus.OK);
? ? }
? ? //Get all users
? ? @GetMapping(produces =MediaType.APPLICATION_JSON_VALUE)
? ? public ResponseEntity<List<User>> getAllUsers() {
? ? List<User> usersList = userService.getAllUsers();
? ? return new ResponseEntity<List<User>>(usersList,HttpStatus.OK);
? ? }
? ? //Update user
? ? @PutMapping(consumes =MediaType.APPLICATION_JSON_VALUE,produces =MediaType.APPLICATION_JSON_VALUE)
? ? public ResponseEntity<User> updateUser(@RequestBody User user){
? ? User UserUpdated = userService.updateUser(user);
? ? return new ResponseEntity<User>(UserUpdated,HttpStatus.ACCEPTED);
? ? }
? ? //Delete user
@DeleteMapping(path="/{id}")
public ResponseEntity<Object> deleteUser(@PathVariable String id) {
userService.deleteUser(id);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}
Role Controller
package com.linkedin.demo.springboot.controllers;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.linkedin.demo.springboot.entities.Role;
import com.linkedin.demo.springboot.services.RoleService;
@CrossOrigin(origins="*")
@RestController
@RequestMapping("/role")
public class RoleController {
@Autowired
RoleService roleService;
//Create new Role
@PostMapping(consumes =MediaType.APPLICATION_JSON_VALUE,produces =MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Role> createRole(@RequestBody Role role){
Role newRole = roleService.createRole(role);
return new ResponseEntity<Role>(newRole,HttpStatus.CREATED);
}
//Get role by id
? ? @GetMapping(path="/{roleId}",produces =MediaType.APPLICATION_JSON_VALUE)
? ? public ResponseEntity<Role> getRoleById(@PathVariable String roleId) {
? ? Role roleFound = roleService.getRoleById(roleId);
? ? return new ResponseEntity<Role>(roleFound,HttpStatus.OK);
? ? }
? ? //Get all roles
? ? @GetMapping(produces =MediaType.APPLICATION_JSON_VALUE)
? ? public ResponseEntity<List<Role>> getAllRoles() {
? ? List<Role> rolesList = roleService.getAllRoles();
? ? return new ResponseEntity<List<Role>>(rolesList,HttpStatus.OK);
? ? }
? ? //Update role
? ? @PutMapping(consumes =MediaType.APPLICATION_JSON_VALUE,produces =MediaType.APPLICATION_JSON_VALUE)
? ? public ResponseEntity<Role> updateUser(@RequestBody Role role){
? ? Role roleUpdated = roleService.updateRole(role);
? ? return new ResponseEntity<Role>(roleUpdated,HttpStatus.ACCEPTED);
? ? }
? ? //Delete role
@DeleteMapping(path="/{id}")
public ResponseEntity<Object> deleteRole(@PathVariable String id) {
roleService.deleteRole(id);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}
Here's a breakdown of all annotations:
@GetMapping:
@PutMapping:
@DeleteMapping:
@PostMapping:
`@CrossOrigin(origins="*")`:
`@RestController`:
`@RequestMapping("/role")`:
`consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE`:
`@PathVariable`:
领英推荐
`@RequestBody`:
These annotations and parameters provide additional functionality and configuration options for your Spring Boot controllers and RESTful APIs. They allow you to handle different types of requests, specify media types, bind URL variables and request bodies, and control cross-origin resource sharing.
Step 7: Connect Data base to our project
In this step, we will connect our Spring Boot project to the MongoDB database that we previously installed. Connecting the database to our project will allow us to perform data operations and interact with the database using our application.
To connect the database to our project, follow these steps:
spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017
spring.data.mongodb.database=your-database-name
server.servlet.context-path=/api?
server.port=8081
Replace your-database-name with the name of the MongoDB database you want to connect to.
You can also change the server port for exemple : 8082,8083....
Or you can also change the context path such as '/myapi' .....
Then save the application.properties file.
By adding these configuration properties, we specify the host, port, and database name for our MongoDB connection.
Step 8: Run the project
In this step, we will run our Spring Boot project to start the server and make it accessible for testing and interaction with the front-end.
Before running the Spring Boot application, ensure that your MongoDB database is up and running. Start MongoDB Compass or any other MongoDB client to ensure the database server is operational.
Once you have confirmed that the database server is running, follow these steps to run the Spring Boot application:
Once the application is running, you can test the API endpoints using tools like Postman or by accessing the URLs directly in your browser. Make requests to the defined endpoints to interact with the application and verify its functionality.
Remember to keep the application running as long as you want to test or use it. If you want to stop the application, you can click on the stop button in your IDE or terminate the process.
Step 9: Testing the Application Using Postman
Once your Spring Boot application is running, you can test the API endpoints using Postman. Postman is a popular tool for API development and testing, allowing you to send HTTP requests and view the responses.
To test your application using Postman, follow these steps:
Conclusion
In conclusion, we have successfully set up the role and user functionalities in our Spring Boot application. We have created the necessary packages, entities, repositories, services, and controllers to handle the CRUD operations related to roles and users. With these components in place, we can now focus on building the front-end of our application using Angular
Building Frontend (Angular Project)
Now that we have completed the backend part using Spring Boot, it's time to shift our focus to the frontend development. We will start by setting up the necessary imports and dependencies.
Step 1: Importing Modules and Dependencies
To begin, let's make sure we have the required modules and dependencies imported into our Angular project. Open the index.html file located in the src folder. Inside the <head> section, add the following import statements to import Bootstrap
<!doctype html>
<html lang="en">
<head>
? <meta charset="utf-8">
? <title>AngularSpringbootApp</title>
? <base href="/">
? <meta name="viewport" content="width=device-width, initial-scale=1">
? <link rel="icon" type="image/x-icon" href="favicon.ico">
? <!--Bootstrap import CSS-->
? <link rel="stylesheet" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
? <!--End Bootstrap imports-->
</head>
<body>
? <app-root></app-root>
? <!--Bootstrap import JS-->
? <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
? <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
? <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
? <!--End Bootstrap imports-->
</body>
</html>
These imports will give our application access to the Bootstrap CSS framework for styling and the Font Awesome library for icons.
Next, let's move to our Angular modules. Open the app.module.ts file located in the src/app folder. Add the following import statements :
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
imports:[
? ? BrowserModule,
? ? AppRoutingModule,
? ? HttpClientModule,
FormsModule
? ],
? providers: [],
? bootstrap: [AppComponent]
Step 2: Manage Angular Models
In this step, we will create and manage Angular models, which will represent the data structures used in our application. Models define the shape and properties of our data objects, providing a structured and type-safe way to handle data.
To create Angular models type the command below (on a terminal)
ng generate class models/Role
In the above command, models represents the folder where you want to generate the model, and Role is the name of the model. You can modify these values according to your project structure and desired model name.
After setting up the necessary imports and dependencies, we can now proceed to add the necessary variables to our class.
export class Role {
? id : string;
? roleName : string;
? constructor(){
? ? this.id = "";
? ? this.roleName = "";
? }
}
Next the User Model ,to create User model type the command below (on a terminal)
ng generate class models/User
We can now proceed to add the necessary variables to our class.
import { Role } from "./role";
export class User {
? id : string;
? userName : string;
? lastName : string;
? role : Role;
? constructor(){
? ? this.id = "";
? ? this.userName = "";
? ? this.lastName = "";
? ? this.role = new Role();
? }
}
Step 3: Manage Angular Services
Now that we have created our models, it's time to manage our Angular services. Services in Angular provide a way to encapsulate and share data, logic, and functionality across different components.
To create a new service, run the following commands in your Angular project's root directory:
ng g service Services/user
ng g service Services/role
ng g service Services/data
Inside the generated service file, you can define variables, methods, and logic specific to your service's functionality.
Let's start with editing our DataService.ts
import { Inject, Injectable } from '@angular/core';
import { Observable } from 'rxjs';
@Injectable({
? providedIn: 'root'
})
export class DataService {
? ?// Inject ApiUrl in constructor to Get it form ather Service
? ?constructor(@Inject(String) private APIUrl: string,private http: HttpClient) { }
? // Get Method
? getAll(): Observable<any> {
? ? return this.http.get<any>(this.APIUrl);
? }
? // Get with id
? get(id: any): Observable<any> {
? ? return this.http.get(`${this.APIUrl}/${id}`);
? }
? // Update Method
? Update(data: any): Observable<any> {
? ? return this.http.put(`${this.APIUrl}`, data);
? }
? // Create Method
? Create(data: any): Observable<any> {
? ? return this.http.post(this.APIUrl, data);
? }
? // Delete Method
? Delete(id: any): Observable<any> {
? ? return this.http.delete(`${this.APIUrl}/${id}`);
? }
}
After creating our DataService, we can move on to other services in our application. These services will primarily depend on the DataService to interact with the API and perform various operations. The advantage of this approach is that we only need to inject the API URL into the DataService, which will provide all the necessary functions.
Let's take an example of a UserService that handles user-related operations. In the UserService, we can inject the DataService and configure it with the API URL:
import { Injectable } from '@angular/core';
import { DataService } from './data.service';
import { HttpClient } from '@angular/common/http';
const ?APIUrlUser ="https://localhost:8081/api/user";
@Injectable({
? providedIn: 'root'
})
export class UserService extends DataService{
? constructor(http:HttpClient){
? ? super(APIUrlUser,http);
? }
}
And we do the same with RoleService
import { Injectable } from '@angular/core';
import { DataService } from './data.service';
import { HttpClient } from '@angular/common/http';
const ?APIUrlRole ="https://localhost:8081/api/role";
@Injectable({
? providedIn: 'root'
})
export class RoleService extends DataService{
? constructor(http:HttpClient){
? ? super(APIUrlRole,http);
? }
}
Step 3: Setting up Angular Routing
Before customizing our application and creating new components, let's set up Angular routing to navigate between different views or pages.
Define the routes for your application by creating an array of route objects. Each route object should have a path and component property. For example:
import { NgModule } from '@angular/core'
import { RouterModule, Routes } from '@angular/router';
import { UserComponent } from './Components/user/user.component';
import { RoleComponent } from './Components/role/role.component';
const routes: Routes = [
? {path : "users" , component : UserComponent},
? {path : "roles" , component : RoleComponent},
? { path: '', ? redirectTo: '/users', pathMatch: 'full' }, // redirect to `users-component` if the path is empty
];
@NgModule({
? imports: [RouterModule.forRoot(routes)],
? exports: [RouterModule]
})
export class AppRoutingModule { };
In this example, we define routes for the user and role pages. The empty path '' redirects to the /users route.
Step 4: Manage Angular Components
Now that we have set up our services and configured the necessary API interactions, it's time to customize our application by creating new components and utilizing Bootstrap for styling. Additionally, we will consume APIs using our services to display data and perform other operations.
To create a new component, run the following commands in your Angular project's root directory:
ng g component Components/role
ng g component Components/user
We have created the UserComponent and RoleComponent as part of our application. Now, let's add a navigation bar to the app.component.html file to facilitate routing between different components using routerLink and routerLinkActive directives.
<nav class="navbar navbar-dark bg-dark">
? <a class="navbar-brand" href="#">Angular springBoot Application</a>
? <div class="navbar-collapse" id="navbarNav">
? ? <ul class="navbar-nav">
? ? ? <li class="nav-item" routerLinkActive="active">
? ? ? ? <a class="nav-link" routerLink="/users">Users</a>
? ? ? </li>
? ? ? <li class="nav-item" routerLinkActive="active">
? ? ? ? <a class="nav-link" routerLink="/roles">Roles</a>
? ? ? </li>
? ? </ul>
? </div></nav>
<router-outlet></router-outlet>
Next, let's move on to designing the UserComponent and RoleComponent. Open the respective component files (user.component.ts, user.component.html, role.component.ts, role.component.html) located in the src/app/Components folder.
In the component HTML files, you can design the user and role components as per your application requirements. Utilize Bootstrap classes and HTML elements to structure and style the components.
In role.component.html :
<button type="button" class="btn btn-success" data-toggle="modal" data-target="#EditAddRole" (click)="openModel()" style="margin: 50px;">Create New Role</button>
<table class="table">
? <thead>
? ? <tr>
? ? ? <th scope="col">#</th>
? ? ? <th scope="col">Name Role</th>
? ? ? <th scope="col" colspan="2">Action</th>
? ? </tr>
? </thead>
? <tbody>
? ? <tr *ngFor="let role of roleList" >
? ? ? <th scope="row">{{role.id}}</th>
? ? ? <td>{{role.roleName}}</td>
? ? ? <td>
? ? ? ? <button type="button" class="btn btn-primary" ?data-toggle="modal" data-target="#EditAddRole" (click)="openModel(role)">Edit</button>
? ? ? </td>
? ? ? <td>
? ? ? ? <button type="button" class="btn btn-danger" (click)="deleteRole(role.id)">Delete</button>
? ? ? </td>
? ? </tr>
? </tbody>
</table>
<!-- Modal -->
<div class="modal fade" id="EditAddRole" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
? <div class="modal-dialog modal-dialog-centered" role="document">
? ? <div class="modal-content">
? ? ? <div class="modal-header">
? ? ? ? <h5 class="modal-title" id="exampleModalLongTitle">Role</h5>
? ? ? ? <button type="button" class="close" data-dismiss="modal" aria-label="Close">
? ? ? ? ? <span aria-hidden="true">×</span>
? ? ? ? </button>
? ? ? </div>
? ? ? <div class="modal-body">
? ? ? ? <div class="input-group mb-3">
? ? ? ? ? <div class="input-group-prepend">
? ? ? ? ? ? <span class="input-group-text" id="inputGroup-sizing-default">Role Name</span>
? ? ? ? ? </div>
? ? ? ? ? <input type="text" class="form-control" [(ngModel)]="newRole.roleName">
? ? ? ? </div>
? ? ? </div>
? ? ? <div class="modal-footer">
? ? ? ? <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
? ? ? ? <button type="button" class="btn btn-primary" *ngIf="creatingMode" (click)="createRole()">Create User</button>
? ? ? ? <button type="button" class="btn btn-primary" *ngIf="!creatingMode" (click)="modifyRole()">Modify User</button> ? ? ?</div>
? ? </div>
? </div>
</div>
Now, let's consume APIs from services to display data or perform other operations. Inject the necessary service into the component's constructor, and utilize the service methods to fetch and manipulate data.
In role.component.ts
import { Component } from '@angular/core';
import { RoleService } from 'src/app/Services/role.service';
import { Role } from 'src/app/models/role';
@Component({
? selector: 'app-role',
? templateUrl: './role.component.html',
? styleUrls: ['./role.component.css']
})
export class RoleComponent {
? roleList : Role[] = [];
? // this variable is get data from model
? newRole : Role = new Role();
? // this variable determines wither we are in changing or creating new user
? creatingMode : boolean = true;
? constructor(private roleService : RoleService){
? ? this.getAllRoles();
? }
//Get All roles from database
? getAllRoles(){
? ? this.roleService.getAll().subscribe((response : Role[])=>{
? ? ? this.roleList = response;
? ? });
? }
//Delete role by id
? deleteRole(roleId : string){
? ? if(confirm("Are you sure you want to delete this role !!!")){
? ? ? this.roleService.Delete(roleId).subscribe(()=>{
? ? ? ? alert("Role Deleted Successfully");
? ? ? ? window.location.reload();
? ? ? });
? ? }
? }
//Create new role in database
? createRole(){
? ? const newRole = {
? ? ? roleName : this.newRole.roleName
? ? }
? ? this.roleService.Create(newRole).subscribe(()=>{
? ? ? alert("Role Created Successfully");
? ? ? window.location.reload();
? ? });
? }
//Update role
? modifyRole(){
? ? this.roleService.Update(this.newRole).subscribe(()=>{
? ? ? alert("Role Updated Successfully");
? ? ? window.location.reload();
? ? })
? }
// function to verify the event
? openModel(role: Role = new Role()){
? ? if(role.id == ""){
? ? ? this.newRole = new Role();
? ? }else{
? ? ? this.creatingMode = false;
? ? ? this.newRole = role;
? ? }
? }
}
Now, let's navigate from the RoleComponent to the UserComponent in our Angular application.
Open the user.component.html file located in the src/app/Components folder.
<button type="button" class="btn btn-success" data-toggle="modal" data-target="#EditAddUser" (click)="openModel()" style="margin: 50px;">Create New User</button>
<table class="table">
? <thead>
? ? <tr>
? ? ? <th scope="col">#</th>
? ? ? <th scope="col">First</th>
? ? ? <th scope="col">Last</th>
? ? ? <th scope="col">Role Name</th>
? ? ? <th scope="col" colspan="2">Action</th>
? ? </tr>
? </thead>
? <tbody>
? ? <tr *ngFor="let user of userList" >
? ? ? <th scope="row">{{user.id}}</th>
? ? ? <td>{{user.userName}}</td>
? ? ? <td>{{user.lastName}}</td>
? ? ? <td>{{user.role.roleName}}</td>
? ? ? <td>
? ? ? ? <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#EditAddUser" (click)="openModel(user)">Edit</button>
? ? ? </td>
? ? ? <td>
? ? ? ? <button type="button" class="btn btn-danger" (click)="deleteUser(user.id)">Delete</button>
? ? ? </td>
? ? </tr>
? </tbody>
</table>
<!-- Modal -->
<div class="modal fade" id="EditAddUser" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
? <div class="modal-dialog modal-dialog-centered" role="document">
? ? <div class="modal-content">
? ? ? <div class="modal-header">
? ? ? ? <h5 class="modal-title" id="exampleModalLongTitle">Role</h5>
? ? ? ? <button type="button" class="close" data-dismiss="modal" aria-label="Close">
? ? ? ? ? <span aria-hidden="true">×</span>
? ? ? ? </button>
? ? ? </div>
? ? ? <div class="modal-body">
? ? ? ? <div class="input-group">
? ? ? ? ? <div class="input-group-prepend">
? ? ? ? ? ? <span class="input-group-text" id="">First and last name</span>
? ? ? ? ? </div>
? ? ? ? ? <input type="text" class="form-control" [(ngModel)]="userToModify.userName">
? ? ? ? ? <input type="text" class="form-control" [(ngModel)]="userToModify.lastName">
? ? ? ? </div><br>
? ? ? ? <div class="input-group mb-3">
? ? ? ? ? <div class="input-group-prepend">
? ? ? ? ? ? <label class="input-group-text" for="inputGroupSelect01">Roles</label>
? ? ? ? ? </div>
? ? ? ? ? <select class="custom-select" id="inputGroupSelect01" [(ngModel)]="userToModify.role.id">
? ? ? ? ? ? <option selected>Choose...</option>
? ? ? ? ? ? <option *ngFor="let role of roleList" ?[value]="role.id">{{role.roleName}}</option>
? ? ? ? ? </select>
? ? ? ? </div>
? ? ? </div>
? ? ? <div class="modal-footer">
? ? ? ? <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
? ? ? ? <button type="button" class="btn btn-primary" *ngIf="creatingMode" (click)="CreateUser()">Create User</button>
? ? ? ? <button type="button" class="btn btn-primary" *ngIf="!creatingMode" (click)="modifyUser()">Modify User</button>
? ? ? </div>
? ? </div>
? </div>
</div>
Now, let's consume APIs from services to display data or perform other operations. Inject the necessary service into the component's constructor, and utilize the service methods to fetch and manipulate data.
Open the user.component.ts file located in the src/app/Components folder.
import { Component } from '@angular/core';
import { RoleService } from 'src/app/Services/role.service';
import { UserService } from 'src/app/Services/user.service';
import { Role } from 'src/app/models/role';
import { User } from 'src/app/models/user';
@Component({
? selector: 'app-user',
? templateUrl: './user.component.html',
? styleUrls: ['./user.component.css']
})
export class UserComponent {
? userList : User[] = [];
? roleList : Role[] = [];
? // this variable is get data from model
? userToModify : User = new User();
? // this variable determines wither we are in changing or creating new user
? creatingMode : boolean = true;
? constructor(private userService : UserService,
private roleService : RoleService){
? ? this.getAllUsers();
? ? this.getAllRoles();
? }
? //Get All Users
? getAllUsers(){
? ? this.userService.getAll().subscribe((response : User[])=>{
? ? ? this.userList = response;
? ? });
? }
? //Get All Roles
? getAllRoles(){
? ? this.roleService.getAll().subscribe((response)=>{
? ? ? this.roleList = response;
? ? })
? }
? //Update User
? modifyUser(){
? ? this.userService.Update(this.userToModify).subscribe(()=>{
? ? ? alert("User Updated Successfully");
? ? ? window.location.reload();
? ? })
? }
? //Create new User
? CreateUser(){
? ? ? const newUser = {
? ? ? ? userName : this.userToModify.userName,
? ? ? ? lastName : this.userToModify.lastName,
? ? ? ? role : {
? ? ? ? ? id : this.userToModify.role.id,
? ? ? ? }
? ? ? };
? ? ? this.userService.Create(newUser).subscribe(()=>{
? ? ? ? alert("User Added Successfully");
? ? ? ? window.location.reload();
? ? ? });
? }
? //Delete User
? deleteUser(userId : string){
? ? if(confirm("Are you sure you want to delete this user !!!")){
? ? ? this.userService.Delete(userId).subscribe(()=>{
? ? ? ? alert("User Deleted Successfully");
? ? ? ? window.location.reload();
? ? ? });
? ? }
? }
? // function to verify the event
? openModel(user : User = new User()){
? ? if(user.id == ""){
? ? ? this.userToModify = new User();
? ? }else{
? ? ? this.creatingMode = false
? ? ? this.userToModify = user;
? ? }
? }
}
In this example, we inject the UserService into the UserComponent constructor and utilize the getAll() method to fetch users data. The fetched data is stored in the userList variable for use in the component template.
Remember to adjust the service names, API endpoints, and component designs according to your specific application needs.
Conclusion
By adding a navigation bar, designing components, and consuming APIs from services, we enhance the functionality and user experience of our Angular application.
Step 5: Opening and Testing the Project
To open and test your Angular project, follow these steps:
1. Open your command prompt or terminal.
2. Navigate to the root directory of your Angular project.
3. Run the following command to start the development server:
ng serve -o
This command will compile your Angular application and serve it locally on `https://localhost:4200`.
4. Open your web browser and visit `https://localhost:4200` to view your application.
5. Test the functionality of your application by interacting with different components, navigating through routes, and verifying that the data is displayed correctly.
- Verify that the navigation bar is visible and the routing between components is working as expected.
- Check if the data from APIs is fetched and displayed correctly in the respective components.
- Perform any other operations or functionalities that we have implemented in our application.
By testing your project, you can ensure that it functions correctly and meets your requirements. If you encounter any issues or errors, review your code and make necessary adjustments.
Note: Make sure you have the necessary backend services running (the Spring Boot application) to handle API requests and provide the expected data to the Angular frontend.
Keep the command prompt or terminal running to keep the development server active. You can make changes to your code and the server will automatically recompile and reflect the updates in the browser.
Now, let's take a closer look at our application in action. Below are some screenshots showcasing the different components, navigation, and data display. Please note that these screenshots provide a glimpse into the user interface and functionality of our application.
Lastly you can download the complete code of this tutorial from?Repository Link.
Conclusion
In conclusion, we have successfully completed the journey of building a full-stack web application using Angular 15, Spring Boot 3, and MongoDB. Throughout this tutorial, we have covered various aspects of the development process, from setting up the backend with Spring Boot and MongoDB to creating the frontend with Angular.
We started by laying the foundation with the backend, implementing the necessary APIs, and connecting to the database. Then, we moved on to the frontend, where we designed and developed components, integrated Bootstrap for styling, and utilized Angular services to consume APIs and handle data operations.
By following this tutorial, you have gained a solid understanding of how to leverage the power of Angular and Spring Boot to create robust and efficient web applications. You have learned how to manage services, create components, implement routing, and interact with backend APIs.
Remember that this tutorial serves as a starting point, and there is always room for further enhancements and customization based on your specific requirements. You can explore additional features such as authentication, authorization, data validation, and error handling to make your application more secure and user-friendly.
We hope this tutorial has provided you with valuable insights and practical knowledge to embark on your own full-stack web development projects. Feel free to leverage the skills you have acquired here and continue expanding your expertise in building innovative and dynamic web applications.
Thank you for joining us on this journey, and we wish you all the best in your future endeavors as a full-stack developer. Happy coding!
Microservices | Full Stack Development | Technical Owner | Product Development
4 个月Hello Imed, Appreciate detailed steps to create full stack app. However, during setting up database, I do not see steps for setting up mongo-db server.
BMS Cheif && Full Stack web developer
11 个月Bonjour Imed: Disponible sur youtube ?
Master 2 Data science | Computer Science student at ENSI | AI | Passionate About Embedded Systems and Computer Graphics
1 年very helpful
Technologue en informatique
1 年Bravo Imed ??
Full Stack Developer ????|| Express.js || ReactJs || Node.js || MongoDB || JavaScript
1 年Great work