Getting Started | Building an Application with Angular
Angular 10 Front-end
In this tutorial, we will learn how to build a full stack Angular 10 + Spring Boot example with a CRUD App. The back-end server uses Spring Boot with Spring Web MVC for REST Controller and Spring Data JPA for interacting with MySQL database. Front-end side is made with Angular 10, HTTPClient & Router.
Angular CLI version (10.1.1)
ng new
The Angular CLI makes it easy to create an application that already works, right out of the box. It already follows our best practices!
ng generate
Generate components, routes, services and pipes with a simple command. The CLI will also create simple test shells for all of these.
ng serve
Easily test your app locally while developing.
$ npm install -g @angular/cli
$ ng new crud-customer
$ cd crud-customer
$ ng serve
Technology
Angular 10
Angular HTTPClient
Angular Router
Project Structure
Install Bootstrap CSS framework
Use the following command to install bootstrap in the project.
$ npm install bootstrap --save
src/style.css
@import '~bootstrap/dist/css/bootstrap.min.css';
Let's create a class by using the following command
Create the Customer.ts class
$ ng g class Customer
The purpose of this class is to map the specified fields with the fields of Spring entity class.
export class Customer {
id: number;
firstname: string;
lastname: string;
age: number;
active: boolean;
}
Create Data Service
This service will use Angular HTTPClient to send HTTP requests. You can see that its functions includes CRUD operations and finder method.
service customer.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Customer } from '../model/Customer';
@Injectable({
providedIn: 'root'
})
export class CustomerService {
private baseUrl = 'http://localhost:8080/api/customers';
constructor(private http: HttpClient) { }
getCustomer(id: number): Observable<Customer> {
return this.http.get<Customer>(`${this.baseUrl}/${id}`);
}
createCustomer(customer: Customer): Observable<Customer> {
return this.http.post<Customer>(`${this.baseUrl}` + `/create`, customer);
}
updateCustomer(id: number, value: Customer): Observable<Customer> {
return this.http.put<Customer>(`${this.baseUrl}/update/${id}`, value);
}
deleteCustomer(id: number): Observable<any> {
return this.http.delete(`${this.baseUrl}/delete/${id}`, { responseType: 'text' });
}
getCustomersList(): Observable<any> {
return this.http.get(`${this.baseUrl}/getAllCustomers`);
}
getCustomersByAge(age: number): Observable<any> {
return this.http.get(`${this.baseUrl}/age/${age}`);
}
getCustomersById(id: number): Observable<any> {
return this.http.get(`${this.baseUrl}/id/${id}`);
}
deleteAllCustomers(): Observable<any> {
return this.http.delete(`${this.baseUrl}` + `/deleteAllCustomers`, { responseType: 'text' });
}
isActive(id: number, value: Customer): Observable<Customer> {
return this.http.put<Customer>(`${this.baseUrl}/isActive/${id}`, value);
}
inActive(id: number, value: Customer): Observable<Customer> {
return this.http.put<Customer>(`${this.baseUrl}/inActive/${id}`, value);
}
}
Create Angular Components
As you’ve known before, there are 3 components corresponding to 3 routes defined in AppRoutingModule.
$ ng generate component add-customer
$ ng generate component customer-details
$ ng generate component customer-list
$ ng generate component edit-customer
$ ng generate component search-customer
Introduction to components and templates
A component controls a patch of screen called a view. For example, individual components define and control each of the following views from the Tour of application tutorial:
You define a component's application logic—what it does to support the view—inside a class. The class interacts with the view through an API of properties and methods.
app.component.html
<div class="container app">
<h1 style="color: blue; text-align: center; margin: 30px 0 20px;">Spring boot and Angular Application Customer Curd</h1>
<ul>
<li> <a routerLink="customer" class="btn btn-primary active" role="button" routerLinkActive="active">Customers</a></li>
<li> <a routerLink="add" class="btn btn-primary active" role="button" routerLinkActive="active">Add</a></li>
<li> <a routerLink="findbyage" class="btn btn-primary active" role="button" routerLinkActive="active">Search</a></li>
</ul>
<router-outlet></router-outlet>
</div>
app.component.css
.app ul {
list-style-type: none;
margin: 0;
padding: 0;
overflow: hidden;
background-color: #ddd;
}
.app li {
float: left;
border-right:1px solid #bbb;
}
.app li:last-child {
border-right: none;
}
.app li a {
display: block;
color: white;
text-align: center;
padding: 14px 16px;
text-decoration: none;
}
Here, we are creating add-customer compoent
add-customer.html
<h2>Create Customer</h2>
<div [hidden]="submitted" style="width: 300px;">
<form (ngSubmit)="save()" style="background: aliceblue; padding: 15px; border-radius: 10px;">
<div class="form-group">
<label>Firstname</label>
<input type="text" class="form-control" id="firstname" required [(ngModel)]="customer.firstname" name="firstname">
</div>
<div class="form-group">
<label>Lastname</label>
<input type="text" class="form-control" id="lastname" required [(ngModel)]="customer.lastname" name="lastname">
</div>
<div class="form-group">
<label>Age</label>
<input type="text" class="form-control" id="age" required [(ngModel)]="customer.age" name="age">
</div>
<button type="submit" class="btn btn-success"Submit</button>
</form>
</div>
<div [hidden]="!submitted">
<h3>You submitted successfully</h3>
<button class="btn btn-success" (click)="newCustomer()">Add</button>
</div>
add-customer.ts
import { Component, OnInit } from '@angular/core';
import { Customer } from '../model/Customer';
import { CustomerService } from '../service/customer-service';
@Component({
selector: 'app-add-customer',
templateUrl: './add-customer.component.html',
styleUrls: ['./add-customer.component.css']
})
export class AddCustomerComponent implements OnInit {
customer: Customer = new Customer();
submitted = false;
constructor(private customerService: CustomerService) { }
ngOnInit() {}
save(){
this.customerService.createCustomer(this.customer).subscribe(customer => {
this.customer = customer;
this.submitted = true;
});
this.customer = new Customer();
}
newCustomer(): void {
this.submitted = false;
this.customer = new Customer();
}
}
Here, we are creating customerdetails compoent
customer-details.html
<br><div class="card" style="width: 18rem;" *ngIf="customer">
<div class="card-header">
<h2>Customer</h2>
</div>
<ul class="list-group list-group-flush">
<li class="list-group-item">Firstname: {{customer.firstname}}</li>
<li class="list-group-item">Lastname: {{customer.lastname}}</li>
<li class="list-group-item">Age: {{customer.age}}</li>
<li class="list-group-item list-group-item-danger" *ngIf="!customer.active">Active: {{customer.active}}</li>
<li class="list-group-item list-group-item-success" *ngIf="customer.active">Active: {{customer.active}}</li>
</ul>
<div class="card-body">
<span class="badge bg-warning"lass="button is-small btn-primary" *ngIf="!customer.active" (click)="isActive()">Inactive</span>
<span class="badge bg-success" *ngIf="customer.active" (click)="inActive()">Active</span>
<span class="badge bg-info" (click)="update()">Update</span>
<span class="badge bg-danger" (click)="deleteCustomer(customer.id)">Delete</span>
</div>
</div>
add-customer.css
.badge {
cursor: pointer;
margin-left: 10px;
}
customer-details.component.ts
import { Component, Input, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { CustomerListComponent } from '../customer-list/customer-list.component';
import { Customer } from '../model/Customer';
import { CustomerService } from '../service/customer-service';
@Component({
selector: 'app-customer-details',
templateUrl: './customer-details.component.html',
styleUrls: ['./customer-details.component.css']
})
export class CustomerDetailsComponent implements OnInit {
@Input() customer: Customer = new Customer();
constructor(private customerService: CustomerService, private router: Router,
private listCustomer: CustomerListComponent) { }
ngOnInit() {}
isActive(){
this.customerService.isActive(this.customer.id, this.customer).subscribe(customer => {
this.customer = customer;
this.listCustomer.reloadData();
}, error => console.log(error));
}
inActive() {
this.customerService.inActive(this.customer.id, this.customer).subscribe(customer => {
this.customer = customer;
this.listCustomer.reloadData();
}, error => console.log(error));
}
update(){
this.router.navigate(['editCustomer/', this.customer.id]);
}
deleteCustomer(){
this.customerService.deleteCustomer(this.customer.id).subscribe(() => {
this.listCustomer.reloadData();
}, error => console.log(error));
}
}
Here, we are creating customer-list compoent
customer-list.component.html
<div *ngFor="let customer of customers | async">
<app-customer-details [customer]='customer'></app-customer-details>
</div>
<br><div *ngIf="customers">
<button type="button" class="button btn-danger" (click)='deleteCustomers()'>Delete All</button>
</div>
customer-list.component.css
table, td, th {
border: 1px solid #ddd;
text-align: left;
}
table {
border-collapse: collapse;
width: 100%;
}
th, td {
padding: 15px;
}
customer-list.component.ts
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs/internal/Observable';
import { Customer } from '../model/Customer';
import { CustomerService } from '../service/customer-service';
@Component({
selector: 'app-customer-list',
templateUrl: './customer-list.component.html',
styleUrls: ['./customer-list.component.css']
})
export class CustomerListComponent implements OnInit {
customers: Observable<Customer[]>;
private(private customerService: CustomerService) { }
ngOnInit() {
this.reloadData();
}
deleteCustomers() {
this.customerService.deleteAllCustomers().subscribe(() => {
this.reloadData();
},error => console.log('ERROR: ' + error));
}
reloadData() {
this.customers = this.customerService.getCustomersList();
}
}
Here, we are creating edit-customer compoent
edit-customer.html
<h2>Update Customer</h2>
<div style="width: 300px;">
<form (ngSubmit)="update()" style="background: aliceblue; padding: 15px; border-radius: 10px;">
<div class="form-group">
<label>Firstname</label>
<input type="text" class="form-control" id="firstname" required [(ngModel)]="customer.firstname" name="firstname">
</div>
<div class="form-group">
<label>Lastname</label>
<input type="text" class="form-control" id="lastname" required [(ngModel)]="customer.lastname" name="lastname">
</div>
<div class="form-group">
<label>Age</label>
<input type="text" class="form-control" id="age" required [(ngModel)]="customer.age" name="age">
</div>
<button type="submit" class="btn btn-success"Submit</button>
</form>
</div>
edit-customer.ts
import { Component, OnInit } from '@angular/core';
import { Customer } from '../model/Customer';
import { CustomerService } from '../service/customer-service';
import { ActivatedRoute, Router } from '@angular/router';
@Component({
selector: 'app-add-customer',
templateUrl: './add-customer.component.html',
styleUrls: ['./add-customer.component.css']
})
export class AddCustomerComponent implements OnInit {
customer: Customer = new Customer();
id = number;
constructor(private customerService: CustomerService, private router: Router, private route: ActivatedRoute) { }
ngOnInit() {
this.id = this.route.snapshot.params.id;
this.customerService.getCustomersById(this.id).subscribe(customer => {
this.customer = customer;
console.log(this.customer);
});
}
update(){
this.customerService.updateCustomer(this.id, this.customer).subscribe(customer => {
this.customer = customer;
this.router.navigate(['/customer']);
});
this.customer = new Customer();
}
}
Here, we are creating search-customer compoent
search-customer.component.html
<h2>Find By Age</h2>
<div style="width: 300px;">
<form (ngSubmit)="onSubmit()">
<div class="form-group">
<label>Age</label>
<input type="text" class="form-control" id="age" required [(ngModel)]="age" name="age">
</div>
<div class="btn-group">
<button type="submit" class="btn btn-success">Submit</button>
</div>
</form>
</div>
<ul>
<li *ngFor="let customer of customers">
<h3>{{customer.id}} - {{customer.firstname}} {{customer.lastname}} {{customer.age}}</h3>
</li>
</ul>
search-customer.component.ts
import { Component, OnInit } from '@angular/core';
import { Customer } from '../model/Customer';
import { CustomerService } from '../service/customer-service';
@Component({
selector: 'app-search-customer',
templateUrl: './search-customer.component.html',
styleUrls: ['./search-customer.component.css']
})
export class SearchCustomerComponent implements OnInit {
age: number;
customers: Customer[];
constructor(private customerService: CustomerService) { }
ngOnInit() {
this.age = 0;
}
searchCustomer(){
this.customerService.getCustomersByAge(this.age).subscribe(customers => this.customers = customers);
}
onSubmit(){
this.searchCustomer();
}
}
Add the AppRoutingModule
In Angular, the best practice is to load and configure the router in a separate, top-level module that is dedicated to routing and imported by the root AppModule.
By convention, the module class name is AppRoutingModule and it belongs in the app-routing.module.ts in the src/app folder.
app-routing.module.ts
import { EditCustomerComponent } from './edit-customer/edit-customer.component';
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AddCustomerComponent} from './add-customer/add-customer.component';
import { CustomerListComponent } from './customer-list/customer-list.component';
import { SearchCustomerComponent } from './search-customer/search-customer.component';
const routes: Routes = [
{path: '', redirectTo: 'customer', pathMatch: 'full'},
{path: 'customer', component: CustomerListComponent},
{path: 'add', component: AddCustomerComponent},
{path: 'findbyage', component: SearchCustomerComponent},
{path: 'editCustomer/:id', component: EditCustomerComponent}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
NgModules and JavaScript modules
The NgModule system is different from and unrelated to the JavaScript (ES2015) module system for managing collections of JavaScript objects. These are complementary module systems that you can use together to write your apps.
In JavaScript each file is a module and all objects defined in the file belong to that module. The module declares some objects to be public by marking them with the export key word. Other JavaScript modules use import statements to access public objects from other modules.
app-module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { AddCustomerComponent } from './add-customer/add-customer.component';
import { SearchCustomerComponent } from './search-customer/search-customer.component';
import { CustomerDetailsComponent } from './customer-details/customer-details.component';
import { CustomerListComponent } from './customer-list/customer-list.component';
import { HttpClientModule } from '@angular/common/http';
import { FormsModule } from '@angular/forms';
import { EditCustomerComponent } from './edit-customer/edit-customer.component';
@NgModule({
declarations: [
AppComponent,
AddCustomerComponent,
SearchCustomerComponent,
CustomerDetailsComponent,
CustomerListComponent,
EditCustomerComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
FormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Conclusion
Now we have an overview of Angular 10 + Spring Boot CRUD example when building a CRUD App.
We also take a look at client-server architecture for REST API using Spring Web MVC & Spring Data JPA, as well as Angular 10 project structure for building a front-end app to make HTTP requests and consume responses.