In this tutorial, we show you Angular 10 Http Client & Spring Boot Server example that uses Spring JPA to do CRUD with MariaDB and Angular 10 as a front-end technology to make request and receive response.
Related Posts:
– Spring Boot + Angular 10 example | Spring Data JPA + REST + MySQL CRUD example
– Spring Boot + Angular 10 example | Spring Data JPA + REST + PostgreSQL CRUD example
– Spring Boot + Angular 10 example | Spring Data + REST + Cassandra CRUD example
– How to use Spring JPA with PostgreSQL | Spring Boot
Related Pages:
I. Technologies
– Java 1.8
– Maven 3.3.9
– Spring Tool Suite – Version 3.9.4.RELEASE
– Spring Boot: 2.0.4.RELEASE
– Angular 10
– RxJS 6
– MariaDB
II. Overview
1. Spring Boot Server
2. Angular 10 Client
III. Practice
1. Project Structure
1.1 Spring Boot Server
– Customer class corresponds to entity and table customer.
– CustomerRepository is an interface extends CrudRepository, will be autowired in CustomerController for implementing repository methods and custom finder methods.
– CustomerController is a REST Controller which has request mapping methods for RESTful requests such as: getAllCustomers
, postCustomer
, deleteCustomer
, deleteAllCustomers
, findByAge
, updateCustomer
.
– Configuration for Spring Datasource and Spring JPA properties in application.properties
– Dependencies for Spring Boot and MariaDB in pom.xml
1.2 Angular 10 Client
In this example, we focus on:
– 4 components: customers-list, customer-details, create-customer, search-customer.
– 3 modules: FormsModule, HttpClientModule, AppRoutingModule.
– customer.ts: class Customer (id, firstName, lastName)
– customer.service.ts: Service for Http Client methods
2. How to do
2.1 Spring Boot Server
2.1.1 Dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
</dependency>
2.1.2 Customer – Data Model
model/Customer.java
package com.ozenero.springrest.mariadb.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "customer")
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column(name = "name")
private String name;
@Column(name = "age")
private int age;
@Column(name = "active")
private boolean active;
public Customer() {
}
public Customer(String name, int age) {
this.name = name;
this.age = age;
this.active = false;
}
public long getId() {
return id;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return this.age;
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
@Override
public String toString() {
return "Customer [id=" + id + ", name=" + name + ", age=" + age + ", active=" + active + "]";
}
}
2.1.3 JPA Repository
repo/CustomerRepository.java
package com.ozenero.springrest.mariadb.repo;
import java.util.List;
import org.springframework.data.repository.CrudRepository;
import com.ozenero.springrest.mariadb.model.Customer;
public interface CustomerRepository extends CrudRepository {
List findByAge(int age);
}
2.1.4 REST Controller
controller/CustomerController.java
package com.ozenero.springrest.mariadb.controller;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ozenero.springrest.mariadb.model.Customer;
import com.ozenero.springrest.mariadb.repo.CustomerRepository;
@CrossOrigin(origins = "http://localhost:4200")
@RestController
@RequestMapping("/api")
public class CustomerController {
@Autowired
CustomerRepository repository;
@GetMapping("/customers")
public List getAllCustomers() {
System.out.println("Get all Customers...");
List customers = new ArrayList<>();
repository.findAll().forEach(customers::add);
return customers;
}
@PostMapping(value = "/customers/create")
public Customer postCustomer(@RequestBody Customer customer) {
Customer _customer = repository.save(new Customer(customer.getName(), customer.getAge()));
return _customer;
}
@DeleteMapping("/customers/{id}")
public ResponseEntity deleteCustomer(@PathVariable("id") long id) {
System.out.println("Delete Customer with ID = " + id + "...");
repository.deleteById(id);
return new ResponseEntity<>("Customer has been deleted!", HttpStatus.OK);
}
@DeleteMapping("/customers/delete")
public ResponseEntity deleteAllCustomers() {
System.out.println("Delete All Customers...");
repository.deleteAll();
return new ResponseEntity<>("All customers have been deleted!", HttpStatus.OK);
}
@GetMapping(value = "customers/age/{age}")
public List findByAge(@PathVariable int age) {
List customers = repository.findByAge(age);
return customers;
}
@PutMapping("/customers/{id}")
public ResponseEntity updateCustomer(@PathVariable("id") long id, @RequestBody Customer customer) {
System.out.println("Update Customer with ID = " + id + "...");
Optional customerData = repository.findById(id);
if (customerData.isPresent()) {
Customer _customer = customerData.get();
_customer.setName(customer.getName());
_customer.setAge(customer.getAge());
_customer.setActive(customer.isActive());
return new ResponseEntity<>(repository.save(_customer), HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
}
2.1.5 Configuration for Spring Datasource & JPA properties
application.properties
spring.datasource.url=jdbc:mariadb://localhost:3306/gkzdb
spring.datasource.username=root
spring.datasource.password=12345
spring.jpa.generate-ddl=true
2.2 Angular 10 Client
2.2.0 Create Service & Components
Run commands below:
– ng g s Customer
– ng g c CreateCustomer
– ng g c CustomerDetails
– ng g c CustomersList
– ng g c SearchCustomers
On each Component selector, delete app-
prefix, then change tslint.json rules
– "component-selector"
to false.
2.2.1 Model
customer.ts
export class Customer {
id: number;
name: string;
age: number;
active: boolean;
}
2.2.2 CustomerService
customer.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class CustomerService {
private baseUrl = 'http://localhost:8080/api/customers';
constructor(private http: HttpClient) { }
getCustomer(id: number): Observable<Object> {
return this.http.get(`${this.baseUrl}/${id}`);
}
createCustomer(customer: Object): Observable<Object> {
return this.http.post(`${this.baseUrl}` + `/create`, customer);
}
updateCustomer(id: number, value: any): Observable<Object> {
return this.http.put(`${this.baseUrl}/${id}`, value);
}
deleteCustomer(id: number): Observable<any> {
return this.http.delete(`${this.baseUrl}/${id}`, { responseType: 'text' });
}
getCustomersList(): Observable<any> {
return this.http.get(`${this.baseUrl}`);
}
getCustomersByAge(age: number): Observable<any> {
return this.http.get(`${this.baseUrl}/age/${age}`);
}
deleteAll(): Observable<any> {
return this.http.delete(`${this.baseUrl}` + `/delete`, { responseType: 'text' });
}
}
2.2.3 Components
– CustomerDetailsComponent:
customer-details/customer-details.component.ts
import { Component, OnInit, Input } from '@angular/core';
import { CustomerService } from '../customer.service';
import { Customer } from '../customer';
import { CustomersListComponent } from '../customers-list/customers-list.component';
@Component({
selector: 'customer-details',
templateUrl: './customer-details.component.html',
styleUrls: ['./customer-details.component.css']
})
export class CustomerDetailsComponent implements OnInit {
@Input() customer: Customer;
constructor(private customerService: CustomerService, private listComponent: CustomersListComponent) { }
ngOnInit() {
}
updateActive(isActive: boolean) {
this.customerService.updateCustomer(this.customer.id,
{ name: this.customer.name, age: this.customer.age, active: isActive })
.subscribe(
data => {
console.log(data);
this.customer = data as Customer;
},
error => console.log(error));
}
deleteCustomer() {
this.customerService.deleteCustomer(this.customer.id)
.subscribe(
data => {
console.log(data);
this.listComponent.reloadData();
},
error => console.log(error));
}
}
customer-details/customer-details.component.html
<div *ngIf="customer">
<div>
<label>Name: </label> {{customer.name}}
</div>
<div>
<label>Age: </label> {{customer.age}}
</div>
<div>
<label>Active: </label> {{customer.active}}
</div>
<span class="button is-small btn-primary" *ngIf='customer.active' (click)='updateActive(false)'>Inactive</span>
<span class="button is-small btn-primary" *ngIf='!customer.active' (click)='updateActive(true)'>Active</span>
<span class="button is-small btn-danger" (click)='deleteCustomer()'>Delete</span>
<hr/>
</div>
– CustomersListComponent:
customers-list/customers-list.component.ts
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { CustomerService } from '../customer.service';
import { Customer } from '../customer';
@Component({
selector: 'customers-list',
templateUrl: './customers-list.component.html',
styleUrls: ['./customers-list.component.css']
})
export class CustomersListComponent implements OnInit {
customers: Observable;
constructor(private customerService: CustomerService) { }
ngOnInit() {
this.reloadData();
}
deleteCustomers() {
this.customerService.deleteAll()
.subscribe(
data => {
console.log(data);
this.reloadData();
},
error => console.log('ERROR: ' + error));
}
reloadData() {
this.customers = this.customerService.getCustomersList();
}
}
customers-list/customers-list.component.html
<h1>Customers</h1>
<div *ngFor="let customer of customers | async" style="width: 300px;">
<customer-details [customer]='customer'></customer-details>
</div>
<div>
<button type="button" class="button btn-danger" (click)='deleteCustomers()'>Delete All</button>
</div>
– CreateCustomerComponent:
create-customer/create-customer.component.ts
import { Component, OnInit } from '@angular/core';
import { Customer } from '../customer';
import { CustomerService } from '../customer.service';
@Component({
selector: 'create-customer',
templateUrl: './create-customer.component.html',
styleUrls: ['./create-customer.component.css']
})
export class CreateCustomerComponent implements OnInit {
customer: Customer = new Customer();
submitted = false;
constructor(private customerService: CustomerService) { }
ngOnInit() {
}
newCustomer(): void {
this.submitted = false;
this.customer = new Customer();
}
save() {
this.customerService.createCustomer(this.customer)
.subscribe(data => console.log(data), error => console.log(error));
this.customer = new Customer();
}
onSubmit() {
this.submitted = true;
this.save();
}
}
create-customer/create-customer.component.html
<h3>Create Customer</h3>
<div [hidden]="submitted" style="width: 300px;">
<form (ngSubmit)="onSubmit()">
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" id="name" required [(ngModel)]="customer.name" name="name">
</div>
<div class="form-group">
<label for="age">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">
<h4>You submitted successfully!</h4>
<button class="btn btn-success" (click)="newCustomer()">Add</button>
</div>
– SearchCustomersComponent:
search-customers/search-customers.component.ts
import { Component, OnInit } from '@angular/core';
import { Customer } from '../customer';
import { CustomerService } from '../customer.service';
@Component({
selector: 'search-customers',
templateUrl: './search-customers.component.html',
styleUrls: ['./search-customers.component.css']
})
export class SearchCustomersComponent implements OnInit {
age: number;
customers: Customer[];
constructor(private dataService: CustomerService) { }
ngOnInit() {
this.age = 0;
}
private searchCustomers() {
this.dataService.getCustomersByAge(this.age)
.subscribe(customers => this.customers = customers);
}
onSubmit() {
this.searchCustomers();
}
}
search-customers/search-customers.component.html
<h3>Find By Age</h3>
<div style="width: 300px;">
<form (ngSubmit)="onSubmit()">
<div class="form-group">
<label for="lastname">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">
<h4>{{customer.id}} - {{customer.name}} {{customer.age}}</h4>
</li>
</ul>
2.2.4 AppRoutingModule
app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { CustomersListComponent } from './customers-list/customers-list.component';
import { CreateCustomerComponent } from './create-customer/create-customer.component';
import { SearchCustomersComponent } from './search-customers/search-customers.component';
const routes: Routes = [
{ path: '', redirectTo: 'customer', pathMatch: 'full' },
{ path: 'customer', component: CustomersListComponent },
{ path: 'add', component: CreateCustomerComponent },
{ path: 'findbyage', component: SearchCustomersComponent },
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
And AppComponent HTML for routing:
app.component.html
<div style="padding: 20px;">
<h1 style="color: blue">{{title}}</h1>
<h3>{{description}}</h3>
<nav>
<a routerLink="customer" class="btn btn-primary active" role="button" routerLinkActive="active">Customers</a>
<a routerLink="add" class="btn btn-primary active" role="button" routerLinkActive="active">Add</a>
<a routerLink="findbyage" class="btn btn-primary active" role="button" routerLinkActive="active">Search</a>
</nav>
<router-outlet></router-outlet>
</div>
2.2.5 AppModule
app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { CreateCustomerComponent } from './create-customer/create-customer.component';
import { CustomerDetailsComponent } from './customer-details/customer-details.component';
import { CustomersListComponent } from './customers-list/customers-list.component';
import { SearchCustomersComponent } from './search-customers/search-customers.component';
import { AppRoutingModule } from './app-routing.module';
import { HttpClientModule } from '@angular/common/http';
@NgModule({
declarations: [
AppComponent,
CreateCustomerComponent,
CustomerDetailsComponent,
CustomersListComponent,
SearchCustomersComponent
],
imports: [
BrowserModule,
FormsModule,
AppRoutingModule,
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
3. Run & Check Result
– Build and Run Spring Boot project with commandlines: mvn clean install
and mvn spring-boot:run
.
– Run the Angular App with command: ng serve
.
– Open browser for url http://localhost:4200/
:
Add Customer:
Show Customers:
Click on Active button to update Customer status:
Search Customers by Age:
Delete a Customer:
Delete All Customers:
IV. Source Code
– SpringBootRestAPIsMariadb
– Angular6SpringBoot-Client
389576 605511Youre so cool! I dont suppose Ive learn anything like this before. So good to locate any person with some authentic thoughts on this subject. realy thank you for starting this up. this web site is something that is wanted on the internet, someone with slightly bit originality. helpful job for bringing something new to the internet! 821471
857772 450130Aw, this was a genuinely nice post. In concept I would like to put in writing like this in addition – taking time and actual effort to make a really great article but what can I say I procrastinate alot and by no means seem to get something done. 388236
607241 631459You made some first rate factors there. I regarded on the internet for the difficulty and located a lot of people will associate with with your website. 835225
731466 329671There is an ending. Just remember that I meant for this to be an art game. I do feel like I spent an inordinate amount of time on the more traditional gameplay elements, which may possibly make the meaning of the game a bit unclear. Should you mess around with it though, you will uncover it. 683032
919468 365910I enjoyed reading your pleasant web site. I see you offer priceless info. stumbled into this website by chance but Im positive glad I clicked on that link. You certainly answered all the questions Ive been dying to answer for some time now. Will surely come back for far more of this. 882639
right now, i am using LED desk lamps because they do not heat as much as incandescent desk lamps-
I actually still cannot quite feel that I could become one of those reading the important guidelines found on your web blog. My family and I are truly thankful for your generosity and for providing me potential to pursue our chosen career path. Many thanks for the important information I obtained from your web site.
In my opinion ,, truth be told, the achievements work with respect to basically the good results is response to these letdown.
Nice post. I understand some thing harder on various blogs everyday. Most commonly it is stimulating to learn to read content using their company writers and exercise something from their store. I’d opt to use some while using the content in my blog regardless of whether you don’t mind. Natually I’ll give you a link on your own web blog. Thank you for sharing.
I enjoy you because of your whole work on this blog. My daughter loves engaging in research and it’s really simple to grasp why. Most people know all of the compelling way you give vital items by means of the web blog and invigorate participation from the others on that subject matter while our own girl is always discovering a whole lot. Take pleasure in the remaining portion of the year. You’re carrying out a powerful job.
11096 815108Perfect just what I was searching for! . 804564
898763 593952Take a peek at the following guidelines what follows discover perfect way to follow such a mainly because you structure your small business this afternoon. earn cash 471153
517725 348982Youre so cool! I dont suppose Ive read anything in this way before. So good to locate somebody by original thoughts on this topic. realy thanks for beginning this up. this fabulous web site is one thing that is needed on the internet, a person with a bit of originality. beneficial project for bringing a new challenge towards internet! 500179
382893 502062This internet web site is my aspiration, extremely excellent style and style and Perfect subject matter. 796065