In this tutorial, grokonez shows you Angular 4 Http Client & Spring Boot Server example that uses Spring JPA to interact with PostgreSQL and Angular 4 as a front-end technology to make request and receive response.
Related Posts:
– How to use Spring JPA with PostgreSQL | Spring Boot
– Spring JPA + PostgreSQL + AngularJS example | Spring Boot
– How to use Angular Http Client to fetch Data from SpringBoot RestAPI – Angular 4
– How to use Angular HttpClient to POST, PUT, DELETE data on SpringBoot Rest APIs – Angular 4
Updated:
– Spring Boot + Angular 6 example | Spring Data JPA + REST + PostgreSQL CRUD example
Contents
I. Technologies
– Java 1.8
– Maven 3.3.9
– Spring Tool Suite – Version 3.8.4.RELEASE
– Spring Boot: RELEASE
– Angular 4
II. Overview
1. Spring Boot Server
For more details about Spring JPA – PostgreSQL, please visit:
How to use Spring JPA with PostgreSQL | Spring Boot
2. Angular 4 Client
For more details:
– About Angular 4 Routing:
How to work with Angular Routing – Spring Boot + Angular 4
– About Angular Http Client to GET/POST/DELETE:
+ How to use Angular Http Client to fetch Data from SpringBoot RestAPI – Angular 4
+ How to use Angular HttpClient to POST, PUT, DELETE data on SpringBoot Rest APIs – Angular 4
III. Practice
1. Project Structure
1.1 Spring Boot Server
– Class Customer corresponds to entity and table customer, it should be implemented Serializable.
– 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: getAll
, postCustomer
, delete
, findByLastName
.
– Configuration for Spring Datasource and Spring JPA properties in application.properties
– Dependencies for Spring Boot and PostgreSQL in pom.xml
1.2 Angular 4 Client
In this example, we focus on:
– 4 components: customers, customer-details, create-customer, search-customer.
– 3 modules: FormsModule, HttpModule, AppRoutingModule.
– customer.ts: class Customer (id, firstName, lastName)
– data.service.ts: DataService for Http Client methods
– proxy.conf.json for integrating Angular Client with Spring Boot Server.
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.postgresql</groupId> <artifactId>postgresql</artifactId> <scope>runtime</scope> </dependency> |
2.1.2 Customer – Data Model
package com.javasampleapproach.jpapostgresqlangular4.model; import java.io.Serializable; 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 implements Serializable { private static final long serialVersionUID = -3009157732242241606L; @Id @GeneratedValue(strategy = GenerationType.AUTO) private long id; @Column(name = "firstname") private String firstName; @Column(name = "lastname") private String lastName; protected Customer() { } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public Customer(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } @Override public String toString() { return String.format("Customer[id=%d, firstName='%s', lastName='%s']", id, firstName, lastName); } } |
2.1.3 JPA Repository
package com.javasampleapproach.jpapostgresqlangular4.repo; import java.util.List; import org.springframework.data.repository.CrudRepository; import com.javasampleapproach.jpapostgresqlangular4.model.Customer; public interface CustomerRepository extends CrudRepository<Customer, Long> { List<Customer> findByLastName(String lastName); } |
2.1.4 REST Controller
package com.javasampleapproach.jpapostgresqlangular4.controller; import java.util.ArrayList; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; 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.RequestBody; import org.springframework.web.bind.annotation.RestController; import com.javasampleapproach.jpapostgresqlangular4.model.Customer; import com.javasampleapproach.jpapostgresqlangular4.repo.CustomerRepository; @RestController public class CustomerController { @Autowired CustomerRepository repository; @GetMapping(value="/customer", produces=MediaType.APPLICATION_JSON_VALUE) public List<Customer> getAll() { List<Customer> list = new ArrayList<>(); Iterable<Customer> customers = repository.findAll(); customers.forEach(list::add); return list; } @PostMapping(value="/postcustomer") public Customer postCustomer(@RequestBody Customer customer) { repository.save(new Customer(customer.getFirstName(), customer.getLastName())); return customer; } @GetMapping(value="/findbylastname/{lastName}", produces=MediaType.APPLICATION_JSON_VALUE) public List<Customer> findByLastName(@PathVariable String lastName) { List<Customer> customers = repository.findByLastName(lastName); return customers; } @DeleteMapping(value="/customer/{id}") public void deleteCustomer(@PathVariable long id){ repository.delete(id); } } |
2.1.5 Configuration for Spring Datasource & JPA properties
spring.datasource.url=jdbc:postgresql://localhost/testdb spring.datasource.username=postgres spring.datasource.password=123 spring.jpa.generate-ddl=true |
2.2 Angular 4 Client
2.2.0 Model
export class Customer { public id: number; public firstName: string; public lastName: string; } |
2.2.1 DataService
import { Injectable } from '@angular/core'; import { Headers, Http } from '@angular/http'; import 'rxjs/add/operator/toPromise'; import { Customer } from './customer'; @Injectable() export class DataService { private customersUrl = 'customer'; // URL to web API private headers = new Headers({'Content-Type': 'application/json'}); constructor(private http: Http) {} // Get all customers getCustomers(): Promise<Customer[]> { return this.http.get(this.customersUrl) .toPromise() .then(response => response.json() as Customer[]) .catch(this.handleError); } getCustomersByLastName(lastName: string): Promise<Customer[]> { const url = `findbylastname/${lastName}`; return this.http.get(url) .toPromise() .then(response => response.json() as Customer) .catch(this.handleError); } create(customer: Customer): Promise<Customer> { return this.http .post("postcustomer", JSON.stringify(customer), {headers : this.headers}) .toPromise() .then(res => res.json() as Customer) .catch(this.handleError); } delete(id: number): Promise<void> { const url = `${this.customersUrl}/${id}`; return this.http.delete(url, {headers: this.headers}) .toPromise() .then(() => null) .catch(this.handleError); } private handleError(error: any): Promise<any> { console.error('Error', error); // for demo purposes only return Promise.reject(error.message || error); } } |
2.2.2 Components
– CustomersComponent:
import { Component, OnInit } from '@angular/core'; import { Customer } from '../customer'; import { DataService } from '../data.service'; @Component({ selector: 'customers-list', templateUrl: './customers.component.html', styleUrls: ['./customers.component.css'], }) export class CustomersComponent implements OnInit { customers: Customer[]; selectedCustomer: Customer; constructor(private dataService: DataService) {} getCustomers() { this.dataService.getCustomers().then(customers => this.customers = customers); } ngOnInit(): void { this.getCustomers(); } onSelect(cust: Customer): void { this.selectedCustomer = cust; } } |
<ul> <li *ngFor="let cust of customers" [class.selected]="cust === selectedCustomer" (click)="onSelect(cust)"> <h4>{{cust.id}} - {{cust.firstName}} {{cust.lastName}}</h4> </li> </ul> <customer-detail [customer]="selectedCustomer"></customer-detail> |
– CustomerDetailsComponent:
import { Component, Input } from '@angular/core'; import { Customer } from '../customer'; import { DataService } from '../data.service'; @Component({ selector: 'customer-detail', templateUrl: './customer-details.component.html', styleUrls: ['./customer-details.component.css'], providers: [DataService] }) export class CustomerDetailsComponent { @Input() customer: Customer; constructor(private dataService: DataService) {} delete(): void { this.dataService.delete(this.customer.id).then(() => this.goBack()); } goBack(): void { window.location.replace(''); } } |
<div *ngIf="customer"> <h2>{{customer.firstName}} details:</h2> <div> <label>id: </label> {{customer.id}} </div> <div> <label>First Name: </label> {{customer.firstName}} </div> <div> <label>Last Name: </label> {{customer.lastName}} </div> <button class="btn btn-danger" (click)="delete()">Delete</button> </div> |
– CreateCustomerComponent:
import {Customer} from '../customer'; import {DataService} from '../data.service'; import {Component, OnInit} from '@angular/core'; import {Location} from '@angular/common'; @Component({ selector: 'app-create-customer', templateUrl: './create-customer.component.html', styleUrls: ['./create-customer.component.css'] }) export class CreateCustomerComponent implements OnInit { customer = new Customer; submitted = false; constructor(private dataService: DataService, private location: Location) {} ngOnInit() { } newCustomer(): void { this.submitted = false; this.customer = new Customer(); } private save(): void { this.dataService.create(this.customer); } onSubmit() { this.submitted = true; this.save(); } goBack(): void { this.location.back(); } } |
<h3>Create Customer Form</h3> <div [hidden]="submitted" style="width: 300px;"> <form (ngSubmit)="onSubmit()"> <div class="form-group"> <label for="firstname">First Name</label> <input type="text" class="form-control" id="firstname" required [(ngModel)]="customer.firstName" name="firstname"> </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"> </div> <div class="btn-group"> <button class="btn btn-primary" (click)="goBack()">Back</button> <button type="submit" class="btn btn-success">Submit</button> </div> </form> </div> <div [hidden]="!submitted"> <div class="btn-group "> <h4>You submitted successfully!</h4> <button class="btn btn-primary" (click)="goBack()">Back</button> <button class="btn btn-success" (click)="newCustomer()">Add</button> </div> </div> |
– SearchCustomersComponent:
import {Component, OnInit} from '@angular/core'; import {Customer} from '../customer'; import {DataService} from '../data.service'; @Component({ selector: 'app-search-customers', templateUrl: './search-customers.component.html', styleUrls: ['./search-customers.component.css'] }) export class SearchCustomersComponent implements OnInit { lastName: string; customers: Customer[]; constructor(private dataService: DataService) {} ngOnInit() { this.lastName = ""; } private searchCustomers() { this.dataService.getCustomersByLastName(this.lastName).then(customers => this.customers = customers); } onSubmit() { this.searchCustomers(); } } |
<h3>Find By Last Name</h3> <div style="width: 300px;"> <form (ngSubmit)="onSubmit()"> <div class="form-group"> <label for="lastname">Last Name</label> <input type="text" class="form-control" id="lastname" required [(ngModel)]="lastName" name="lastname"> </div> <div class="btn-group"> <button type="submit" class="btn btn-success">Submit</button> </div> </form> </div> <ul> <li *ngFor="let cust of customers"> <h4>{{cust.id}} - {{cust.firstName}} {{cust.lastName}}</h4> </li> </ul> |
2.2.3 AppRoutingModule
import {CreateCustomerComponent} from './create-customer/create-customer.component'; import {CustomersComponent} from './customers/customers.component'; import {SearchCustomersComponent} from './search-customers/search-customers.component'; import {NgModule} from '@angular/core'; import {RouterModule, Routes} from '@angular/router'; const routes: Routes = [ {path: '', redirectTo: 'customer', pathMatch: 'full'}, {path: 'customer', component: CustomersComponent}, {path: 'add', component: CreateCustomerComponent}, {path: 'findbylastname', component: SearchCustomersComponent}, ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule {} |
And AppComponent HTML for routing:
<div style="padding: 20px;"> <h2 style="color: blue">JSA - Angular Application!</h2> <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="findbylastname" class="btn btn-primary active" role="button" routerLinkActive="active">Search</a> </nav> <router-outlet></router-outlet> </div> |
2.2.4 AppModule
import {AppRoutingModule} from './app-routing.module'; import {BrowserModule} from '@angular/platform-browser'; import {NgModule} from '@angular/core'; import {FormsModule} from '@angular/forms'; import {HttpModule} from '@angular/http'; import {AppComponent} from './app.component'; import {CustomerDetailsComponent} from './customer-details/customer-details.component'; import {CustomersComponent} from './customers/customers.component'; import {DataService} from './data.service'; import {CreateCustomerComponent} from './create-customer/create-customer.component'; import {enableProdMode} from '@angular/core'; import {SearchCustomersComponent} from './search-customers/search-customers.component'; @NgModule({ declarations: [ AppComponent, CustomerDetailsComponent, CustomersComponent, CreateCustomerComponent, SearchCustomersComponent ], imports: [ BrowserModule, FormsModule, HttpModule, AppRoutingModule ], providers: [DataService], bootstrap: [AppComponent] }) export class AppModule {} |
2.2.5 Integrate Angular Client with Spring Boot Server
– Add proxy.conf.json to root folder of the project:
{ "/": { "target": "http://localhost:8080", "secure": false } } |
– Edit package.json file for “start” script:
... "scripts": { "ng": "ng", "start": "ng serve --proxy-config proxy.conf.json", "build": "ng build", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e" }, ... |
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: npm start
.
– Open browser for url http://localhost:4200/
:
Add Customer:
Show Customers & click on any Customer:
>> PostgreSQL DB:
Search Customer:
Go back to ShowCustomers, chose a Customer and click on Delete Customer:
>> PostgreSQL DB after deleting:
IV. Source Code
SpringJpaPostgreSQLAngular4
Angular4Client
Last updated on April 11, 2019.
localhost:4200 shows nothing. The page is all white.
Can you help me out?
Hi,
Please show me more details about your browser console log.
Regards,
JSA.
I have error line in customer-details.component.ts on line @Input() customer: Customer;
I just copy yours. I don’t know what’s going on. Please help me out.
Hi Sam,
Please give me more detail about your error (error log for example) .
Regards,
JSA.
CustomerDetailsComponent.html:5 ERROR TypeError: Cannot read property ‘firstName’ of undefined
at Object.eval [as updateDirectives] (CustomerDetailsComponent.html:7)
at Object.debugUpdateDirectives [as updateDirectives] (core.es5.js:13067)
at checkAndUpdateView (core.es5.js:12251)
at callViewAction (core.es5.js:12599)
at execComponentViewsAction (core.es5.js:12531)
at checkAndUpdateView (core.es5.js:12257)
at callViewAction (core.es5.js:12599)
at execComponentViewsAction (core.es5.js:12531)
at checkAndUpdateView (core.es5.js:12257)
at callViewAction (core.es5.js:12599)
For temp solution
I have used *ngIf in customer details component html file.
Create Customer Form
……
it is a kinda of validator checks whether customer is undefined or has value.
Hi JSA, love these tutorials, best I find online!
Can you please make similar tutorial for using browser and Angular 4 and connecting to FTP server , select and read unprocessed CSV file from folder_one, perform simple process( example UpperCase) and save processed CSV to folder_two on FTP server. With browser interaction: User can see unprocess files in folder_one and select file to process / download. Also can see process files in folder_two and select file to download?
Many Thanks
Frank
Hi,
I can only see ‘Cannot GET /’ when i access http://localhost:4200/.
Can you please help.
Thanks,
Sashi
I got it. CSS files were missing and i added them, that resolved the issue. Thanks!!
Hi,
When I try to get the customers the request is failing, I have postgres running and I created the table “customer”
Never mind I forgot to add the proxy when starting the Angular front end
Please add css files. You added [class.selected] code in the customer.compoenet.html file. But, you didn’t provide css code. That’s why there is an error.
Hi Su,
[class.xxx] is special Angular4 binding syntax. For css file, we use Bootstrap in index.html.
You can download source code for details 🙂
Regards,
JSA.
Hi,
I am facing an issue while trying to use the above code. Attaching the error log of the browser console.
kindly run your code by using following command “ng serve –proxy-config proxy.conf.json” .