Skip to main content

HttpClient Full Response

Step 13 — Getting the Full HTTP Response with Angular HttpClient 13

In this step, we'll proceed by implementing the logic for retrieving pagination information from the Link header contained in the HTTP response received from the JSON REST API server.

By default, HttpClient does only provide the response body but in our case we need to parse the Link header for pagination links so we need to tell HttpClient that we want the full [HttpResponse](https://angular.io/api/common/http/HttpResponse) using the observe option.

The Link header in HTTP allows the server to point an interested client to another resource containing metadata about the requested resource.Wikipedia

Go to the src/app/data.service.ts file and import the RxJS tap() operator:

import { retry, catchError, tap } from 'rxjs/operators';

Next, define the following string variables:

  public first: string = "";
public prev: string = "";
public next: string = "";
public last: string = "";

Next, define the parseLinkHeader() method which parses the Link header and populate the previous variables accordingly:

  parseLinkHeader(header) {
if (header.length == 0) {
return ;
}

let parts = header.split(',');
var links = {};
parts.forEach( p => {
let section = p.split(';');
var url = section[0].replace(/<(.*)>/, '$1').trim();
var name = section[1].replace(/rel="(.*)"/, '$1').trim();
links[name] = url;

});

this.first = links["first"];
this.last = links["last"];
this.prev = links["prev"];
this.next = links["next"];
}

Next, update the sendGetRequest() as follows:

  public sendGetRequest(){
// Add safe, URL encoded _page and _limit parameters

return this.httpClient.get(this.REST_API_SERVER, { params: new HttpParams({fromString: "_page=1&_limit=20"}), observe: "response"}).pipe(retry(3), catchError(this.handleError), tap(res => {
console.log(res.headers.get('Link'));
this.parseLinkHeader(res.headers.get('Link'));
}));
}

We added the observe option with the response value in the options parameter of the get() method so we can have the full HTTP response with headers. Next, we use the RxJS tap() operator for parsing the Link header before returning the final Observable.

Since the sendGetRequest() is now returning an Observable with a full HTTP response, we need to update the home component so open the src/app/home/home.component.ts file and import HttpResponse as follows:

import { HttpResponse } from '@angular/common/http';

Next, update the subscribe() method as follows:

  ngOnInit() {

this.dataService.sendGetRequest().pipe(takeUntil(this.destroy$)).subscribe((res: HttpResponse<any>)=>{
console.log(res);
this.products = res.body;
})
}

We can now access the data from the body object of the received HTTP response.

Next, go back to the src/app/data.service.ts file and add the following method:

  public sendGetRequestToUrl(url: string){
return this.httpClient.get(url, { observe: "response"}).pipe(retry(3), catchError(this.handleError), tap(res => {
console.log(res.headers.get('Link'));
this.parseLinkHeader(res.headers.get('Link'));

}));
}

This method is similar to sendGetRequest() except that it takes the URL to which we need to send an HTTP GET request.

Go back to the src/app/home/home.component.ts file and add define the following methods:

  public firstPage() {
this.products = [];
this.dataService.sendGetRequestToUrl(this.dataService.first).pipe(takeUntil(this.destroy$)).subscribe((res: HttpResponse<any>) => {
console.log(res);
this.products = res.body;
})
}
public previousPage() {

if (this.dataService.prev !== undefined && this.dataService.prev !== '') {
this.products = [];
this.dataService.sendGetRequestToUrl(this.dataService.prev).pipe(takeUntil(this.destroy$)).subscribe((res: HttpResponse<any>) => {
console.log(res);
this.products = res.body;
})
}

}
public nextPage() {
if (this.dataService.next !== undefined && this.dataService.next !== '') {
this.products = [];
this.dataService.sendGetRequestToUrl(this.dataService.next).pipe(takeUntil(this.destroy$)).subscribe((res: HttpResponse<any>) => {
console.log(res);
this.products = res.body;
})
}
}
public lastPage() {
this.products = [];
this.dataService.sendGetRequestToUrl(this.dataService.last).pipe(takeUntil(this.destroy$)).subscribe((res: HttpResponse<any>) => {
console.log(res);
this.products = res.body;
})
}

Finally, add open the src/app/home/home.component.html file and update the template as follows:

{% raw %}

<div style="padding: 13px;">
<mat-spinner *ngIf="products.length === 0"></mat-spinner>

<mat-card *ngFor="let product of products" style="margin-top:12px;">
<mat-card-header>
<mat-card-title>#{{product.id}} {{product.name}}</mat-card-title>
<mat-card-subtitle>{{product.price}} $/ {{product.quantity}}
</mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<p>
{{product.description}}
</p>
<img style="height:100%; width: 100%;" src="{{ product.imageUrl }}" />
</mat-card-content>
<mat-card-actions>
<button mat-button> Buy product</button>
</mat-card-actions>
</mat-card>

</div>
<div>
<button (click) ="firstPage()" mat-button> First</button>
<button (click) ="previousPage()" mat-button> Previous</button>
<button (click) ="nextPage()" mat-button> Next</button>
<button (click) ="lastPage()" mat-button> Last</button>
</div>

{% endraw %}

This is a screenshot of our application:

https://www.diigo.com/file/image/rscqpoqzoceparbdpzdspbsdra/Angular+8+HTTP+Example.jpg?k=143b81257774810f9369004fda4c1f4c

Step 14 — Requesting a Typed HTTP Response with Angular HttpClient 13

In this step, we'll see how to use typed HTTP responses in our example application.

Angular HttpClient allows you to specify the type of the response object in the request object, which make consuming the response easier and straightforward. This also enables type assertion during the compile time.

Let's start by defining a custom type using a TypeScript interface with the required properties.

Head back to your command-line interface and run the following command from the root of your project:

$ ng generate interface  product

Next, open the src/app/product.ts file and update it as follows:

export interface Product {
id: number;
name: string;
description: string;
price: number;
quantity: number;
imageUrl: string;
}

Next, specify the Product interface as the HttpClient.get() call's type parameter in the data service. Go back to the src/app/data.service.ts file and import the Product interface:

import { Product } from './product';

Next:

  public sendGetRequest(){

return this.httpClient.get<Product[]>(this.REST_API_SERVER, { params: new HttpParams({fromString: "_page=1&_limit=20"}), observe: "response"}).pipe(retry(3), catchError(this.handleError), tap(res => {
console.log(res.headers.get('Link'));
this.parseLinkHeader(res.headers.get('Link'));

}));
}

public sendGetRequestToUrl(url: string){
return this.httpClient.get<Product[]>(url, { observe: "response"}).pipe(retry(3), catchError(this.handleError), tap(res => {
console.log(res.headers.get('Link'));
this.parseLinkHeader(res.headers.get('Link'));

}));
}

Next, open the src/app/home/home.component.ts file and import the Product interface:

import { Product } from '../product';

Next change the type of the products array as follows:

export class HomeComponent implements OnInit, OnDestroy {

products: Product[] = [];

Next change the type of the HTTP response in the sendGetRequest() call:

  ngOnInit() {

this.dataService.sendGetRequest().pipe(takeUntil(this.destroy$)).subscribe((res: HttpResponse<Product[]>) => {
console.log(res);
this.products = res.body;
})
}

You also need to do the same for the other firstPage(), previousPage(), nextPage() and lastPage() methods.