Angular 12 SpringBoot Get/Post/Put/Delete requests

Angular provides the HttpClient in @angular/common/http for front-end applications communicate with backend services. In the tutorial, we show how to build an Angular application that uses the HttpClient to make get/post/put/delete requests with Observable apis to SpringBoot RestAPIs.

Related posts:
Angular 12 Service – with Observable Data for Asynchronous Operation
Angular 12 Routing/Navigation – with Angular Router Service
Angular 12 Template Driven Form – NgModel for Two-Way Data Binding

Technologies

  • Java 1.8
  • Maven 3.3.9
  • Spring Tool Suite – Version 3.9.4.RELEASE
  • Spring Boot: 2.0.3.RELEASE
  • Angular 12
  • RxJS 6
  • Bootstrap 4
  • Visual Studio Code – version 1.24.0

Overview

Goals

We create 2 projects:

– Angular Client Project:

angular-6-http-client-get-post-put-delete-request + angular-6-project-structure

– SpringBoot Server Project:

angular-6-http-client-get-post-put-delete-request + spring-boot-rest-apis

Overview

springboot-rest-apis +angular-6-http-client +angular-http-service-architecture

UserCase

– Retrieve all customers from SpringBoot server:

angular-6-http-client-get-post-put-delete-request + retrieve-all-customer-angular-http-client-get-request

– Update a customer -> Change the firstname of first customer: ‘Joe’ to ‘Robert’.

angular-6-http-client-get-post-put-delete-request + upadate-joe-to-robert-angular-http-client-update

-> result:

angular-6-http-client-get-post-put-delete-request + press-update-button

– Delete ‘Peter’ customer:

angular-6-http-client-get-post-put-delete-request + angular-http-client-delete

– Add a new customer:

angular-6-http-client-get-post-put-delete-request + angular-http-client-post-a-customer

-> result:

angular-6-http-client-get-post-put-delete-request + angular-http-post-a-customer

– Check final customer’s list:

angular-6-http-client-get-post-put-delete-request + final-check

SpringBoot RestAPIs

SpringBoot Services exposes 5 RestAPIs as below:

  • @GetMapping(value="/api/customers") public List getAll()
  • @GetMapping(value="/api/customers/{id}") public Customer getCustomer(@PathVariable Long id)
  • @PostMapping(value="/api/customers") public Customer postCustomer(@RequestBody Customer customer)
  • @DeleteMapping(value="/api/customers/{id}") public void deleteCustomer(@PathVariable Long id)
  • @PutMapping(value="/api/customers") public void putCustomer(@RequestBody Customer customer)

– Configure cross-origin for Angular-Client which running at port: 4200.

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

Angular 12 HttpClient

Use Angular HttpClient APIs to do Get/Post/Put/Delete requests to SpringBoot servers:

// 1. GET All Customers from remote SpringBoot API @GetMapping(value="/api/customers")
getCustomers (): Observable {
	return this.http.get(this.customersUrl)
}

// 2. GET a Customer from remote SpringBoot API @GetMapping(value="/api/customers/{id}")
getCustomer(id: number): Observable {
	const url = `${this.customersUrl}/${id}`;
	return this.http.get(url);
}

// 3. POST a Customer to remote SpringBoot API @PostMapping(value="/api/customers")
addCustomer (customer: Customer): Observable {
	return this.http.post(this.customersUrl, customer, httpOptions);
}

// 4.DELETE a Customer from remote SpringBoot API @DeleteMapping(value="/api/customers/{id}")
deleteCustomer (customer: Customer | number): Observable {
	const id = typeof customer === 'number' ? customer : customer.id;
	const url = `${this.customersUrl}/${id}`;

	return this.http.delete(url, httpOptions);
}

// 5. PUT a Customer to remote SpringBoot API @PutMapping(value="/api/customers")
updateCustomer (customer: Customer): Observable {
	return this.http.put(this.customersUrl, customer, httpOptions);
}

Practice

SpringBoot RestAPIs

We create SpringBoot project with below dependency:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>

Data Model

– Create Customer data model:

package com.javasampleapproach.restapi.model;

public class Customer {
	private Long id;
	private String firstname;
	private String lastname;
	private int age;
	
	public Customer(){
	}
	
	public Customer(String firstname, String lastname, int age){
		this.firstname = firstname;
		this.lastname = lastname;
		this.age = age;
	}
	
	public Customer(Long id, String firstname, String lastname, int age){
		this.id = id;
		this.firstname = firstname;
		this.lastname = lastname;
		this.age = age;
	}
	
	// id
	public void setId(Long id){
		this.id = id;
	}
	
	public Long getId(){
		return this.id;
	}
	
	// firstname
	public void setFirstname(String firstname){
		this.firstname = firstname;
	}
	
	public String getFirstname(){
		return this.firstname;
	}
	
	// lastname
	public void setLastname(String lastname){
		this.lastname = lastname;
	}
	
	public String getLastname(){
		return this.lastname;
	}
	
	// age
	public void setAge(int age){
		this.age = age;
	}
	
	public int getAge(){
		return this.age;
	}	
}

Rest APIs

Implement Rest APIs for GET/POST/PUT/DELETE customers:

package com.javasampleapproach.restapi.controller;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;

import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.javasampleapproach.restapi.model.Customer;


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

	private Map customers = new HashMap(){
		
		private static final long serialVersionUID = 1L;

		{
	        put(1L, new Customer(1L, "Joe", "Thomas", 36));
	        put(2L, new Customer(2L, "Peter", "Smith", 18));
	        put(3L, new Customer(3L, "Lauren", "Taylor", 31));
	        put(4L, new Customer(4L, "Mary", "Taylor", 24));
	        put(5L, new Customer(5L, "David", "Moore", 25));
	        put(6L, new Customer(6L, "Holly", "Davies", 27));
	        put(7L, new Customer(7L, "Michael", "Brown", 45));
	    }
	};
	
	@GetMapping(value="/api/customers")
	public List getAll(){
		List results = customers.entrySet().stream()
									.map(entry ->entry.getValue())
									.collect(Collectors.toList());
		return results;
	}
	
	@GetMapping(value="/api/customers/{id}")
	public Customer getCustomer(@PathVariable Long id){
		return customers.get(id);
	}
	
	@PostMapping(value="/api/customers")
	public Customer postCustomer(@RequestBody Customer customer){
		Entry maxByKey = customers.entrySet()
                .stream()
                .reduce((curr, nxt) -> curr.getKey() > nxt.getKey() ? curr : nxt)
                .get();
		
		Long nextId = (long) (maxByKey.getKey() + 1);
		customer.setId(nextId);
		customers.put(nextId, customer);
		return customer;
	}
	
	@PutMapping(value="/api/customers")
	public void putCustomer(@RequestBody Customer customer){
		customers.replace(customer.getId(), customer);
	}
	
	@DeleteMapping(value="/api/customers/{id}")
	public void deleteCustomer(@PathVariable Long id){
        	customers.remove(id);
	}
}

Angular 12 Client

Setup Angular Project

– Create Angular project:

ng new angular6-httpclient

– Generate:

  • Customer Class
  • Customer Service
  • Customer Components
  • App Routing Module

-> Details:

ng generate class Customer
ng generate service Customer
ng generate component Customer
ng generate component CustomerDetails
ng generate component AddCustomer
ng generate module AppRouting

– Install Bootstrap 4:

npm install bootstrap jquery --save

-> Configure installed Bootstrap & JQuery in angular.json file:

...
 
"styles": [
  "src/styles.css",
  "node_modules/bootstrap/dist/css/bootstrap.min.css"
],
"scripts": [
  "node_modules/jquery/dist/jquery.min.js",
  "node_modules/bootstrap/dist/js/bootstrap.min.js"
]
 
...

Data Model

Implement Customer model customer.ts :

export class Customer {
    id: number;
    firstname: string;
    lastname: string;
    age: number;
}

Configure AppModule

In the developed application, we use:

  • Angular Forms -> for building form
  • HttpClient -> for http Get/Post/Put/Delete requests
  • AppRouting -> for app routing

-> Modify AppModule app.module.ts:

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

import { FormsModule }   from '@angular/forms';
import { HttpClientModule }    from '@angular/common/http';

import { AppRoutingModule }     from './app-routing/app-routing.module';

import { AppComponent } from './app.component';
import { CustomerComponent } from './customer/customer.component';
import { CustomerDetailsComponent } from './customer-details/customer-details.component';
import { AddCustomerComponent } from './add-customer/add-customer.component';

@NgModule({
  declarations: [
    AppComponent,
    CustomerComponent,
    CustomerDetailsComponent,
    AddCustomerComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    AppRoutingModule,
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

HttpClient DataService

Implement CustomerService customer.service.ts with HttpClient for Get/Post/Put/Delete:

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Customer } from './customer';

const httpOptions = {
  headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};

@Injectable({
  providedIn: 'root'
})
export class CustomerService {
  private customersUrl = 'http://localhost:8080/api/customers';  // URL to web api
  constructor( 
    private http: HttpClient
  ) { }

  getCustomers (): Observable {
    return this.http.get(this.customersUrl)
  }

  getCustomer(id: number): Observable {
    const url = `${this.customersUrl}/${id}`;
    return this.http.get(url);
  }

  addCustomer (customer: Customer): Observable {
    return this.http.post(this.customersUrl, customer, httpOptions);
  }

  deleteCustomer (customer: Customer | number): Observable {
    const id = typeof customer === 'number' ? customer : customer.id;
    const url = `${this.customersUrl}/${id}`;

    return this.http.delete(url, httpOptions);
  }

  updateCustomer (customer: Customer): Observable {
    return this.http.put(this.customersUrl, customer, httpOptions);
  }
}

Angular Router

Implement App-Routing module app-routing.module.ts:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { CustomerComponent } from '../customer/customer.component';
import { AddCustomerComponent } from '../add-customer/add-customer.component';
import { CustomerDetailsComponent } from '../customer-details/customer-details.component';

const routes: Routes = [
   { 
     path: 'customers', 
     component: CustomerComponent 
   },
   { 
     path: 'customer/add', 
     component: AddCustomerComponent 
   },
   { 
     path: 'customers/:id', 
     component: CustomerDetailsComponent 
   },
   { 
     path: '', 
     redirectTo: 'customers', 
     pathMatch: 'full'
   }, 
];

@NgModule({
  imports: [ RouterModule.forRoot(routes) ],
  exports: [ RouterModule ]
})

export class AppRoutingModule {}

Router Outlet & Router Links

-> Questions:

  • How to show Componenets with Angular Routers? -> Solution: using Router Outlet
  • How to handle the routing that comes from user’s actions? (like clicks on anchor tag) -> Solution: using Router Link

-> We can achieve above functions by using Angular’s router-outlet and routerLink.

Modify the template file app.component.html of AppComponenet component as below:

<div class="container">
  <div class="row">
    <div class="col-sm-4">  
      <h1>Angular HttpClient</h1>
      <ul class="nav justify-content-center">
          <li class="nav-item">
              <a routerLink="customers" class="btn btn-light btn-sm" role="button" routerLinkActive="active">Retrieve</a> 
          </li>
          <li class="nav-item">
              <a routerLink="customer/add" class="btn btn-light btn-sm" role="button" routerLinkActive="active">Create</a>
          </li>
      </ul>
      <hr>
      <router-outlet></router-outlet>
    </div>
  </div>
</div>

Customer Component

Customer Component ->

angular-6-http-client-get-post-put-delete-request + retrieve-all-customer-angular-http-client-get-request

– Implement CustomerComponent class customer.component.ts:

import { Component, OnInit } from '@angular/core';
import { Customer } from '../customer';
import { CustomerService } from '../customer.service';


@Component({
  selector: 'app-customer',
  templateUrl: './customer.component.html',
  styleUrls: ['./customer.component.css']
})

export class CustomerComponent  implements OnInit {

  customers: Customer[];

  constructor(private customerService: CustomerService) {}

  ngOnInit(): void {
     this.getCustomers();
  }

  getCustomers() {
    return this.customerService.getCustomers()
               .subscribe(
                 customers => {
                  console.log(customers);
                  this.customers = customers
                 }
                );
 }
}

– Implement the template customer.component.html :

<h3>All Customers</h3>
<div *ngFor="let cust of customers">
  <a [routerLink]="['/customers', cust.id]" style="color:black"><span class="badge badge-dark">{{cust.id}}</span> -> {{ cust.firstname }}</a>
</div>

Customer Detail Component

Customer Detail ->

angular-6-http-client-get-post-put-delete-request + customer-details

-> results:

angular-6-http-client-get-post-put-delete-request + press-update-button

angular-6-http-client-get-post-put-delete-request + angular-http-client-delete

– Implement CustomerDetails class customer-details.component.ts:

import { Component, OnInit } from '@angular/core';
import { Customer } from '../customer';
import { CustomerService } from '../customer.service';

import { ActivatedRoute, Params } from '@angular/router';
import { Location } from '@angular/common';

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

  customer = new Customer() ;
  submitted = false;
  message: string;

  constructor(
    private customerService: CustomerService,
    private route: ActivatedRoute,
    private location: Location
  ) {}

  ngOnInit(): void {
    const id = +this.route.snapshot.paramMap.get('id');
    this.customerService.getCustomer(id)
      .subscribe(customer => this.customer = customer);
  }

  update(): void {
    this.submitted = true;
    this.customerService.updateCustomer(this.customer)
        .subscribe(() => this.message = "Customer Updated Successfully!");
  }

  delete(): void {
    this.submitted = true;
    this.customerService.deleteCustomer(this.customer.id)
        .subscribe(()=> this.message = "Customer Deleted Successfully!");
  }

  goBack(): void {
    this.location.back();
  }
}

– Implement CustomerDetailsComponent template customer-details.component.html :

<h4><span class="badge badge-light ">{{customer.id}}</span> -> {{customer.firstname}}</h4>
<div [hidden]="submitted">
    <form (ngSubmit)="update()" #detailCustomerForm="ngForm">
      <div class="form-group">
        <label for="firstname">First Name</label>
        <input type="text" class="form-control" id="firstname" required
        [(ngModel)]="customer.firstname" name="firstname" #firstname="ngModel">
        <div [hidden]="firstname.valid || firstname.pristine"
             class="alert alert-danger">
            First Name is required
        </div>
      </div>
      
      <div class="form-group">
        <label for="lastname">Last Name</label>
        <input type="text" class="form-control" id="lastname" required
        [(ngModel)]="customer.lastname" name="lastname" #lastname="ngModel">
        <div [hidden]="lastname.valid || lastname.pristine"
             class="alert alert-danger">
            Last Name is required
        </div>
      </div>
      
      <div class="form-group">
        <label for="age">Age</label>
        <input type="number" class="form-control" id="age" required
        [(ngModel)]="customer.age" name="age" #age="ngModel">
        <div [hidden]="age.valid || age.pristine"
             class="alert alert-danger">
            Age is required
        </div>
      </div>

      <div class="btn-group btn-group-sm">
        <button type="button" class="btn btn-dark" (click)="goBack()">Back</button>
        <button type="submit" class="btn btn-dark" [disabled]="!detailCustomerForm.form.valid">Update</button>
        <button class="btn btn-dark" (click)="delete()">Delete</button>
	    </div>
      
    </form>
</div>
 
<div [hidden]="!submitted">
    <p>{{message}}</p>
    <div class="btn-group btn-group-sm">
      <button class="btn btn-dark" (click)="goBack()">Back</button>
    </div>
</div>

We can change the value of ng-valid & ng-invalid for more visual feedback,
-> Create ./assets/forms.css file:

.ng-valid[required], .ng-valid.required  {
    border-left: 5px solid rgba(32, 77, 32, 0.623);
}
 
.ng-invalid:not(form)  {
    border-left: 5px solid rgb(148, 27, 27);
}

Add ./assets/forms.css file to index.html :

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Angular6Httpclient</title>
  <base href="/">

  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
  <link rel="stylesheet" href="assets/forms.css">
</head>
<body>
  <app-root></app-root>
</body>
</html>

Add-Customer Component

AddCustomer Component ->

angular-6-http-client-get-post-put-delete-request + angular-http-client-post-a-customer

-> result:

angular-6-http-client-get-post-put-delete-request + angular-http-post-a-customer

– Implement AddCustomerComponent class add-customer.component.ts:

import { Component, OnInit } from '@angular/core';
import { Customer } from '../customer';
import { CustomerService } from '../customer.service';

import { Location } from '@angular/common';

@Component({
  selector: 'app-add-customer',
  templateUrl: './add-customer.component.html',
  styleUrls: ['./add-customer.component.css']
})

export class AddCustomerComponent{

  customer = new Customer();
  submitted = false;

  constructor(
    private customerService: CustomerService,
    private location: Location
  ) { }

  newCustomer(): void {
    this.submitted = false;
    this.customer = new Customer();
  }

 addCustomer() {
   this.submitted = true;
   this.save();
 }

  goBack(): void {
    this.location.back();
  }

  private save(): void {
    this.customerService.addCustomer(this.customer)
        .subscribe();
  }
}

– Implement the template add-customer.component.html:

<h3>Add Customer</h3>
<div [hidden]="submitted">
    <form #addCustomerForm="ngForm">

      <div class="form-group">
        <label for="firstname">First Name</label>
        <input type="text" class="form-control" id="firstname" placeholder="Give Customer's FirstName" 
        required
        [(ngModel)]="customer.firstname" name="firstname" #firstname="ngModel">
        <div [hidden]="firstname.valid || firstname.pristine"
             class="alert alert-danger">
            First Name is required
        </div>
      </div>
      
      <div class="form-group">
        <label for="lastname">Last Name</label>
        <input type="text" class="form-control" id="lastname" placeholder="Give Customer's LastName"
        required
        [(ngModel)]="customer.lastname" name="lastname" #lastname="ngModel">
        <div [hidden]="lastname.valid || lastname.pristine"
             class="alert alert-danger">
            Last Name is required
        </div>
      </div>
      
      <div class="form-group">
        <label for="age">Age</label>
        <input type="number" class="form-control" id="age" 
        placeholder="Give Customer's Age"
        required
        [(ngModel)]="customer.age" name="age" #age="ngModel">
        <div [hidden]="age.valid || age.pristine"
             class="alert alert-danger">
            Age is required
        </div>
      </div>
      
      <div class="btn-group btn-group-sm">
      	<button class="btn btn-dark" (click)="goBack()">Back</button>
      	<button class="btn btn-dark" (click)="addCustomer()" [disabled]="!addCustomerForm.form.valid">Add</button>
      </div>
    </form>
</div>

<div [hidden]="!submitted">
  <p>Submitted Successfully! -> <span class="badge badge-light">{{customer.firstname}}  {{customer.lastname}}</span></p>
	<div class="btn-group btn-group-sm">
    	<button class="btn btn-dark" (click)="goBack()">Back</button>
    	<button class="btn btn-dark" (click)="newCustomer(); addCustomerForm.reset()">Continue</button>
    </div>
</div>

SourceCode

Angular-6-Http-Client
SpringBootRestAPIs

0 0 votes
Article Rating
Subscribe
Notify of
guest
1.8K Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments