Angular HTTP Services
TABLE OF CONTENT
- Overview
- Enable HTTP services
- Simulate a Data Server
- HTTP Calling using HttpClient
- Error Handling
- Tap Operator
- Get Product by id : getProduct(id) method
- Add Product : addProduct(product)
- Delete Product : removeProduct() method
- Loader
- Final Code Review
- Download / Live Example
- Final Output
- Summary
HTTP services is the important part of any application which communicates with the server. Most front-end applications communicate with backend services over the HTTP protocol.
Till now, we are getting the dummy products data from MockData.Products
.
In this tutorial, we will add the data persistence features in our application with the help of Http Services of Angular.
We will use the different methods like get()
, post()
, put()
and delete()
of Http to perform CRUD operations of product
.
So let’s start… the first step is to enable HTTP services
- Note
Here, we will use Angular 5 to perform HTTP calls. we will use HttpClient service of @angular/common/http
library for HTTP calls.
If you are using Angular 2 or 4, read HTTP Calling using Angular 2 or 4.
Angular provides @angular/common/http
library for communicating with a remote server over HTTP.
We will call Http Web Services (REST Services) using HttpClient.
To make HttpClient available everywhere in the app,
- open the root
AppModule
, - import the
HttpClientModule
symbol from@angular/common/http
. - add it to the
@NgModule.imports
array.
As shown below,
import { ProductService } from './service/product.service'; import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { HttpClientModule } from '@angular/common/http'; ... @NgModule({ ... imports: [ ... HttpClientModule ], ... }) export class AppModule { }
Now we require remote data server which provides HTTP Web Service, which we can call using Angular Front End.
You can create Web Service in any language like Java, PHP, .Net or NodeJS. We can consume this web service using HttpClient in an angular app.
We don’t have any web services for our application, we can create web services as I talked above, but instead of creating web services, we can simulate a data server, which work same as other web services.
The advantage of simulated data server is, you can start your front end application development without waiting for the web service from the remote web server. once web services available from the remote server, you just need to change the URL in Http calls.
We will mimic communication with a remote data server by using the In-memory Web API module.
After installing the module, the app will make requests to and receive responses from the HttpClient without knowing that the In-memory Web API is intercepting those requests, applying them to an in-memory data store, and returning simulated responses.
This facility is a great convenience for the tutorial. You won’t have to set up a server to learn about HttpClient.
It may also be convenient in the early stages of your own app development when the server’s web API is not yet implemented.
- Note
the In-memory Web API module has nothing to do with HTTP in Angular.
We are using this only to simulate our remote server, if you have remote services available, then you can directly use that web service also.
Execute below command in terminal to Install the In-memory Web API package
npm install angular-in-memory-web-api –save
Once successfully installed, create an InMemoryProductService
in new file called
in-memory-product.service.ts
as shown below.
import { InMemoryDbService } from 'angular-in-memory-web-api'; export class InMemoryProductService extends InMemoryDbService { createDb() { const products = [ { 'id': 11, 'title': 'OPPO F5 Youth (Gold, 32 GB)', 'modelName': 'F5 Youth', 'color': 'Gold', 'productType': 'Mobile', 'brand': 'OPPO', 'price': 16990 }, { 'id': 12, 'title': 'Dell Inspiron 7000', 'modelName': 'Inspiron', 'color': 'Gray', 'productType': 'Laptop', 'brand': 'DELL', 'price': 59990 } ]; return { products }; } }
We are not discussing InMemoryWebApi Module in detail, for more information refer README.md of In Memory Web Api module.
Now, Import the HttpClientInMemoryWebApiModule
and the InMemoryProductService
in the AppModule
Add the HttpClinetInMemoryWebApiModule
to the @NgModule.imports
array and configure it with the InMemoryProductService
, as shown below
import { InMemoryProductService } from './in-memory-product.service'; import { HttpClientModule } from '@angular/common/http'; import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api'; ... @NgModule({ ... imports: [ ... HttpClientModule, HttpClientInMemoryWebApiModule.forRoot(InMemoryProductService) ], ... }) export class AppModule { }
The forRoot()
configuration method takes an InMemoryProductService
class that primes the in-memory database.
This configuration replaces the mock-product-data.ts
, which is now safe to delete.
When your server is ready, detach the In-memory Web API
, and the app’s requests will go through to the server.
Now back to the HttpClient story.
- Tip
For the testing purpose, we can also generate the delay in a simulated sever, while performing HTTP calls, by delay property in options of forRoor()
, as shown below
HttpClientInMemoryWebApiModule.forRoot(InMemoryProductService, { delay : 2000})
This will add the delay of two seconds.
Import HttpClient
and HttpHeaders
from @angular/common/http
in the ProductService
.
import { HttpClient, HttpHeaders } from '@angular/common/http';
Now inject HttpClient
into the constructor of ProductService
in a private property called httpClient
.
export class ProductService{ ... constructor(private httpClient: HttpClient) { ... } ... }
Now let’s get the products from the data server.
Define the productsUrl
with the address of the products resource on the server.
Our current In Memory Web API URL is api/products
; define productsUrl
as below
productsUrl = 'api/products';
Now convert the getProducts()
method for HttpClient
.
export class ProductService{ productsUrl = 'api/products'; constructor(private httpClient: HttpClient) { } getProducts(): Observable<Product[]> { return this.httpClient.get<Product[]>(this.productsUrl); } ... ... }
We have swapped httpClient.get
for of
and the app keeps working without any other changes because both functions return an Observable<Product[]>
.
- Http methods return one value
All HttpClient methods return a RxJS Observable
of something.
HTTP is a request/response protocol. You make a request, it returns a single response.
In general, an
Observable
can return multiple values over time. AnObservable
fromHttpClient
always emits a single value and then completes, never to emit again.-Angular Docs
This particular HttpClient.get
call returns an Observable<Product[]>
, literally “an observable of products arrays”. In practice, it will only return a single product array.
- HttpClient.get returns response in JSON
HttpClient.get
returns the body of the response as an untyped JSON
object by default. Applying the optional type specifier, <Product[]>
, gives you a typed result object.
The shape of the JSON data is determined by the server’s data API. Here we will get the JSON object of Product Array.
- Note
If you are using Angular 2 or 4 and working with Http
class of @angular/http
(deprecated in Angular 5), then you need to convert response data into JSON object. By using res.json()
method.
Check here to see How to work with HTTP calling in Angular 2 or 4?
Things go wrong, when we are getting data from the remote data server, so we need to catch this errors and do something appropriate.
To catch errors in ProductService.getProducts()
method, we ‘pipe’ the observable result from http.get()
through an RxJS catchError()
operator.
We will import the catchError
symbol from rxjs/operators
, along with some other operators we will need later.
import { catchError, map, tap } from 'rxjs/operators';
Now we will extend the observable result with the .pipe()
method and give it a catchError()
operator. As shown below
getProducts(): Observable<Product[]> { return this.httpClient.get<Product[]>(this.productsUrl) .pipe( catchError(this.handleError('getProducts', [])) ); }
The catchError()
operator intercepts an Observable
that failed. It passes the error to an error handler that can do what it wants with the error.
We will create error handler called handleError()
method, this method reports the error and then returns an appropriate result so that the application keeps working.
handleError()
error handler will be shared by many ProductService
methods so it’s generalized to meet their different needs.
Instead of handling the error directly, it returns an error handler function to catchError
that it has configured with both the name of the operation that failed and a safe return value.
/** * Handle Http operation that failed. * Let the app continue. * @param operation - name of the operation that failed * @param result - optional value to return as the observable result */ private handleError(operation = 'operation', result?: T) { return (error: any): Observable => { // TODO: send the error to remote logging infrastructure console.error(error); // log to console instead // Let the app keep running by returning an empty result. return of(result as T); }; }
After reporting the error to console, the handler returns a safe value to the app so it can keep working.
Because each service method returns a different kind of Observable
result, handleError()
takes a type parameter so it can return the safe value as the type that the app expects.
We can process the result before we return the result to calling component method.
In ProductService
methods, we will tap into the flow of observable values and send a message via console.log
or we can use the loggers library to maintain logs.
We will do that with the RxJS tap
operator, which looks at the observable values, does something with those values, and passes them along. The tap
call back doesn’t touch the values themselves.
Here is the final version of getProducts()
with the tap
operator
getProducts(): Observable<Product[]> { return this.httpClient.get<Product[]>(this.productsUrl) .pipe( tap(products => {console.log('products fetched...'); console.log(products); }), catchError(this.handleError('getProducts', [])) ); }
We will get the below output :
Note: Because I have put the delay of 2 seconds, in InMemoryProductService
, you will see the result after 2 seconds. You can see in a console of the browser that it prints products fetched message and array of product.
Now let’s convert other methods for the HTTP Calls.
Most web APIs support a get by id
request in the form api/products/:id
(such as api/products/11
).
We will replace the getProduct()
method in ProductService
with below code
getProduct(id: number): Observable<Product> { const url = `${this.productsUrl}/${id}`; return this.httpClient.get<Product>(url).pipe( tap(_ => console.log(`fetched product id=${id}`)), catchError(this.handleError<Product>(`getHero id=${id}`)) ); }
There are three significant differences from getProducts()
.
- It constructs a request URL with the desired product’s
id
. - The server should respond with a single product rather than an array of products.
- therefore,
getProduct()
returns anObservable<Product>
(“an observable of Product objects”) rather than an observable of Product arrays.
Note: we are using getProduct()
method in product-detail.component.ts
, because we have set the delay for the testing purpose, initially product
object of product-detail.component.ts
will be undefined.
So if you run the application you will get the error on a console as : Cannot read property ‘productType’ of undefined
To resolve this issue, add ? (elvis operator) in product.productType
as product?.productType
, so that template will access the productType
only when a product is not undefined.
In our simulated data server, we can add the product by passing product object in post call of ‘api/products’
Replace the addProduct(product) method with below code
addProduct(product: Product): Observable { const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) }; return this.httpClient.post(this.productsUrl, product, httpOptions).pipe( tap(p => console.log(`added product with id=${p.id}`)), catchError(this.handleError('addProduct')) ); }
We can pass the headers
to Http Service using HttpHeaders
class of @angular/common/http
library.
In this method, we are passing the product
in the JSON format. That’s why we will set the HttpHeaders
'Content-Type'
as 'application/json'
.
httpClient.post(this.productsUrl, product, httpOptions)
, will pass product
object in the body of post call , and send the Headers in the httpOptions
.
Here, the simulated server returns an Observable
, which is successfully added. We can use this returned result for showing appropriate message to use if required.
Note: In a previous chapter, we have not converted addProduct()
method into an asynchronous method, so in AddProductModelComponent
, we are still using synchronous calling of addProduct()
method. But now we have converted addProduct()
method into an asynchronous method which returns an observable.
So to call getProduct()
method we need to subscribe
it in AppProductModelComponent
as shown below
this.productService.addProduct(product).subscribe(result => { this.formSubmitted = true; this.router.navigateByUrl('/products'); });
In above code, we will navigate to products view only when we get the successful result from the addProduct()
method of product.service.ts
The same way we will update the AddProductTemplateComponent
.
Replace the removeProduct()
method in ProductService
with below code.
removeProduct(product: Product | number): Observable<Product> { const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) }; const id = typeof product === 'number' ? product : product.id; const url = `${this.productsUrl}/${id}`; return this.httpClient.delete<Product>(url, httpOptions).pipe( tap(_ => console.log(`deleted Product id=${id}`)), catchError(this.handleError<Product>('deleteProduct')) ); }
It calls HttpClient.delete
.
- the URL is the products resource URL plus the id of the product to delete
- We can get the product id directly as a number or by getting the product object.
- To get the multiple types of the same parameter we will use product: Product | number
- We don’t send data as we did with a
post
. We still send thehttpOptions
.
Note: In a previous chapter, we have not converted removeProduct()
method into an asynchronous method, so in ProductsComponent
, we are still using synchronous calling of removeProduct()
method. But now we have converted removeProduct()
method into an asynchronous method which returns an observable
.
So to call removeProduct()
method we need to subscribe
it in ProductsComponent
, so replace the deleteProduct()
method of ProductsComponent
as shown below
deleteProduct(product: Product) { this.productService.removeProduct(product).subscribe(); this.productService.getProducts() .subscribe( products => { this.products = products; } ); }
Now refresh the browser and check all the functionality.
HTTP service call takes some time to get response, So in this delay instead of showing blank space, we can show loader or message to the user.
We can either use loading GIF or rotating icons to display loader.
Here we will create one LoaderComponent
in LayoutModule
which we can use to display loader for all the views. to control the visibility of LoaderCompnent we will create LoaderService, which can be used by any other component.
so lets first create LoaderComponent, using below CLI command.
ng g c loader
Now add the following code in loader.component.html
<i class="fa fa-refresh fa-spin fa-3x fa-fw" ></i>
To display loader on page, we will add the <app-loader>
selector of LoaderComponent
in the app.component.html
as shown below
<app-header></app-header> <div class="jumbotron jumbotron-fluid"> <div class="container"> <h1 class="display-3">Welcome to Electronic-Shop</h1> <p class="lead">THE ONLINE MEGASTORE</p> <hr class="my-2"> </div> </div> <div class="container-fluid"> <app-loader >Loading...</app-loader> <router-outlet></router-outlet> </div>
Note : we need to export LoaderComponent
from the LayoutModule
, to use it outside the LayoutModule
.
Now refresh the page, you will able to see the loader.
We need to display the loader only when the HTTP call is in progress, once HTTP call is complete we need to hide the loader. We can control the visibility of the loader by using one loader service.
So add the LoaderService
using below CLI command in /service
folder
ng g service loader
Now add the following code in the LoaderService
import { Injectable } from '@angular/core'; @Injectable() export class LoaderService { visible: boolean; constructor() { } showLoader() { this.visible = true; } hideLoader() { this.visible = false; } }
visible
variable, which will be controlled by showLoader()
and hideLoader()
method
To use LoaderService in throughout the application, we will provide it in the @NgModule.providers
of AppModule
.
Now inject the LoaderService in the LoaderComponent
. As shown below export class LoaderComponent implements OnInit { constructor(public loaderService: LoaderService) { } ngOnInit() { } }
We have created loaderService
public, so that we can access it on the template of LoaderComponent
.
Replace the loader.component.html
as below
<i class="fa fa-refresh fa-spin fa-3x fa-fw" *ngIf="loaderService.visible"></i>
I have added *ngIf
directive, which will make the loader visible only when loaderService.visible
is true.
Now in whenever you want to make loader visible, use the showLoader()
method of LoaderService
and hide the loader by using hideLoader()
.
For example in ProductsComponent
:
import { LoaderService } from './../service/loader.service'; import { ProductService } from './../service/product.service'; import { Component, OnInit } from '@angular/core'; import { Product } from '../models/product'; @Component({ selector: 'app-products', templateUrl: './products.component.html', styleUrls: ['./products.component.css'] }) export class ProductsComponent implements OnInit { products: Product[] = []; constructor(public productService: ProductService, private loaderService: LoaderService) { } ngOnInit() { this.loaderService.showLoader(); this.productService.getProducts() .subscribe( products => { this.products = products; this.loaderService.hideLoader(); } ); } deleteProduct(product: Product) { this.loaderService.showLoader(); this.productService.removeProduct(product).subscribe(); this.productService.getProducts() .subscribe( products => { this.products = products; this.loaderService.hideLoader(); } ); } }
The Same way add loader in other components.
import { InMemoryDbService } from 'angular-in-memory-web-api'; export class InMemoryProductService extends InMemoryDbService { createDb() { const products = [ { 'id': 11, 'title': 'OPPO F5 Youth (Gold, 32 GB)', 'modelName': 'F5 Youth', 'color': 'Gold', 'productType': 'Mobile', 'brand': 'OPPO', 'price': 16990 }, { 'id': 12, 'title': 'Dell Inspiron 7000', 'modelName': 'Inspiron', 'color': 'Gray', 'productType': 'Laptop', 'brand': 'DELL', 'price': 59990 } ]; return { products }; } }
import { LoaderService } from './service/loader.service'; import { InMemoryProductService } from './in-memory-product.service'; import { ProductService } from './service/product.service'; import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { AppComponent } from './app.component'; import { ProductsComponent } from './products/products.component'; import { ProductComponent } from './product/product.component'; import { AddProductModelComponent } from './add-product-model/add-product-model.component'; import { AddProductTemplateComponent } from './add-product-template/add-product-template.component'; import { AppRoutingModule } from './app-routing.module'; import { LayoutModule } from './layout/layout.module'; import { ProductDetailComponent } from './product-detail/product-detail.component'; import { HttpClientModule } from '@angular/common/http'; import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api'; @NgModule({ declarations: [ AppComponent, ProductsComponent, ProductComponent, AddProductModelComponent, AddProductTemplateComponent, ProductDetailComponent ], imports: [ BrowserModule, FormsModule, ReactiveFormsModule, AppRoutingModule, LayoutModule, HttpClientModule, // The HttpClientInMemoryWebApiModule module intercepts HTTP requests // and returns simulated server responses. // Remove it when a real server is ready to receive requests. HttpClientInMemoryWebApiModule.forRoot(InMemoryProductService) ], providers: [ProductService, LoaderService], bootstrap: [AppComponent] }) export class AppModule { }
import { Injectable } from '@angular/core'; import { Product } from '../models/product'; import { Observable } from 'rxjs/Observable'; import { of } from 'rxjs/observable/of'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { catchError, map, tap } from 'rxjs/operators'; @Injectable() export class ProductService { productsUrl = 'api/products'; constructor(private httpClient: HttpClient) { } getProducts(): Observable<Product[]> { return this.httpClient.get<Product[]>(this.productsUrl) .pipe( tap(products => {console.log('products fetched...'); console.log(products); }), catchError(this.handleError('getProducts', [])) ); } /** * Handle Http operation that failed. * Let the app continue. * @param operation - name of the operation that failed * @param result - optional value to return as the observable result */ private handleError(operation = 'operation', result?: T) { return (error: any): Observable => { // TODO: send the error to remote logging infrastructure console.error(error); // log to console instead // Let the app keep running by returning an empty result. return of(result as T); }; } addProduct(product: Product): Observable { const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) }; return this.httpClient.post(this.productsUrl, product, httpOptions).pipe( tap(p => console.log(`added product with id=${p.id}`)), catchError(this.handleError('addProduct')) ); } getProduct(id: number): Observable { const url = `${this.productsUrl}/${id}`; return this.httpClient.get(url).pipe( tap(_ => console.log(`fetched product id=${id}`)), catchError(this.handleError(`getHero id=${id}`)) ); } removeProduct(product: Product | number): Observable { const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) }; const id = typeof product === 'number' ? product : product.id; const url = `${this.productsUrl}/${id}`; return this.httpClient.delete(url, httpOptions).pipe( tap(_ => console.log(`deleted Product id=${id}`)), catchError(this.handleError('deleteProduct')) ); } }
import { LoaderService } from './../service/loader.service'; import { ProductService } from './../service/product.service'; import { Component, OnInit } from '@angular/core'; import { Product } from '../models/product'; @Component({ selector: 'app-products', templateUrl: './products.component.html', styleUrls: ['./products.component.css'] }) export class ProductsComponent implements OnInit { products: Product[] = []; constructor(public productService: ProductService, private loaderService: LoaderService) { } ngOnInit() { this.loaderService.showLoader(); this.productService.getProducts() .subscribe( products => { this.products = products; this.loaderService.hideLoader(); } ); } deleteProduct(product: Product) { this.loaderService.showLoader(); this.productService.removeProduct(product).subscribe(); this.productService.getProducts() .subscribe( products => { this.products = products; this.loaderService.hideLoader(); } ); } }
import { LoaderService } from './../../service/loader.service'; import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-loader', templateUrl: './loader.component.html', styleUrls: ['./loader.component.css'] }) export class LoaderComponent implements OnInit { constructor(public loaderService: LoaderService) { } ngOnInit() { } }
<i class="fa fa-refresh fa-spin fa-3x fa-fw" *ngIf="loaderService.visible"></i>
import { Injectable } from '@angular/core'; @Injectable() export class LoaderService { visible: boolean; constructor() { } showLoader() { this.visible = true; } hideLoader() { this.visible = false; } }
import { LoaderService } from './../service/loader.service'; import { ProductService } from './../service/product.service'; import { Component, OnInit } from '@angular/core'; import { FormGroup, FormControl} from '@angular/forms'; import { Validators } from '@angular/forms'; import { Product } from '../models/product'; import { Router } from '@angular/router'; @Component({ selector: 'app-add-product-model', templateUrl: './add-product-model.component.html', styleUrls: ['./add-product-model.component.css'] }) export class AddProductModelComponent implements OnInit { productTypes = ['Laptop', 'Mobile']; formSubmitted = false; myForm: FormGroup; title: FormControl; modelName: FormControl; color: FormControl; productType: FormControl; brand: FormControl; price: FormControl; constructor(private productService: ProductService, private router: Router, private loaderService: LoaderService) { } ngOnInit() { this.title = new FormControl('', [Validators.required, Validators.minLength(10)]); this.modelName = new FormControl(); this.color = new FormControl('', Validators.pattern('[a-zA-Z]*')); this.productType = new FormControl('', Validators.required); this.brand = new FormControl('', Validators.required); this.price = new FormControl('', [Validators.required, Validators.min(1)]); this.myForm = new FormGroup({ 'title': this.title, 'modelName': this.modelName, 'productType': this.productType, 'color': this.color, 'brand': this.brand, 'price': this.price }); } addProduct(product: Product) { this.loaderService.showLoader(); this.productService.addProduct(product).subscribe(result => { this.formSubmitted = true; this.loaderService.hideLoader(); this.router.navigateByUrl('/products'); }); } }
import { ProductService } from './../service/product.service'; import { Product } from './../models/product'; import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { LoaderService } from '../service/loader.service'; @Component({ selector: 'app-add-product-template', templateUrl: './add-product-template.component.html', styleUrls: ['./add-product-template.component.css'] }) export class AddProductTemplateComponent implements OnInit { product: Product = new Product(); formSubmitted = false; productTypes = ['Laptop', 'Mobile']; constructor(private productService: ProductService, private router: Router, private loaderService: LoaderService) { } ngOnInit() { } addProduct() { this.loaderService.showLoader(); this.productService.addProduct(this.product).subscribe(result => { this.formSubmitted = true; this.loaderService.hideLoader(); this.router.navigateByUrl('/products'); }); } }
<app-header></app-header> <div class="jumbotron jumbotron-fluid"> <div class="container"> <h1 class="display-3">Welcome to Electronic-Shop</h1> <p class="lead">THE ONLINE MEGASTORE</p> <hr class="my-2"> </div> </div> <div class="container-fluid"> <app-loader >Loading...</app-loader> <router-outlet></router-outlet> </div>
import { LoaderService } from './../service/loader.service'; import { ProductService } from './../service/product.service'; import { Product } from './../models/product'; import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Location } from '@angular/common'; @Component({ selector: 'app-product-detail', templateUrl: './product-detail.component.html', styleUrls: ['./product-detail.component.css'] }) export class ProductDetailComponent implements OnInit { product: Product; constructor(private activatedRoute: ActivatedRoute, private location: Location, private productService: ProductService, private loaderService: LoaderService) { } ngOnInit() { let id = +this.activatedRoute.snapshot.paramMap.get('id'); console.log('ID : ' + id); this.loaderService.showLoader(); this.productService.getProduct(id).subscribe( product => { this.product = product; this.loaderService.hideLoader(); } ); } goBack() { this.location.back(); } }
<div class="card"> <div class="card-body"> <h4 class="card-title">PRODUCT DETAILS : <i *ngIf="product?.productType=='Mobile'" class="fa fa-mobile" aria-hidden="true"></i> <i *ngIf="product?.productType=='Laptop'" class="fa fa-laptop" aria-hidden="true"></i> </h4> <div class="row"> <div class="col-md-6"> <table class="table"> <tr> <th>Product ID</th> <td>{{product?.id}}</td> </tr> <tr> <th>Title</th> <td>{{product?.title}}</td> </tr> <tr> <th>Model Name</th> <td>{{product?.modelName}}</td> </tr> <tr> <th>Color</th> <td>{{product?.color}}</td> </tr> <tr> <th>Brand</th> <td>{{product?.brand}}</td> </tr> <tr> <th>Price</th> <td>{{product?.price | currency : 'INR'}}</td> </tr> </table> <button type="button" class="btn btn-primary" (click)="goBack()">Back</button> </div> </div> </div> </div>
In this chapter, we have seen
- Http Web Service Calling using HttpClient
- Simulate a Data Server using In Memory Web Api
- HttpClient get(), post(), put() and delete() methods
- Error Handling
- map, catchError and tap operator of RxJS
- Loader using different component.