Angular 5 HTTP using Observables – Part 2

WRITTEN BY GARETH DUNNE @JSDIARIES

Angular 4 HTTP & Observables

In this part of the tutorial we will be focusing on our API calls and how they interact with the BreweryDB API.

Again, if your looking to expand your Angular and Typescript knowledge I highly recommend this Angular & TypeScript book by Yakov Fain.

You can see the final version of what my beer app is here.

Angular Proxy & CORS

First up, in order to get data from the BreweryDB API we want to enable CORS (Cross Origin Resource Sharing) in our browser.

To accomplish this we have two choices:

The easiest option here is to install a CORS Chrome extension here.

This will enable you to perform cross origin shares in Chrome without having to setup a local proxy. This is handy for testing an application locally but would obviously fall short if pushed to a production environment or hosted externally.

The second option, which is something I would encourage you to do is to setup a local proxy. This will allow you to easily enable cross origin resource sharing for all your browsers and doesn’t take long to create.

To do this lets first create a proxy.config.json file in the root of our project directory.

Proxy file location
Proxy file location
{
    "/api": {
        "target": "http://api.brewerydb.com",
        "secure": false,
        "changeOrigin": true,
        "logLevel": "debug",
        "pathRewrite": {
            "^/api": ""
        }
    }
}

This is our proxy API json object. It contains properties that allows our calls to send and receive data to the Brewery API.

However, it won’t work without running the proxy file alongside our application. So we will need to execute this command to start it alongside our application.

ng serve --proxy proxy.config.json

Mapping Data to TypeScript Interface

Next, we need to create a beer view model. This will basically act as an interface for our retrieved data. Properties such as name, description and rating will feature here.

In your app folder create a file called beer.ts and put in the following.

export class Beer {
    _id: string
    title: string;
    url: string;
    description: string;
    rating: number;
    abv: string;
}

This is only a basic version of the actual beer data returned to us by the BreweryDB API.

The data response contains a lot more properties than this but you can always customize the interface to match the data you desire to use in your UI.

We can now import our Beer type definition to any of our components to reference the data retrieved from BreweryDB.

This is a pretty common thing to do when using TypeScript and making HTTP requests for data.

This is also a benefit of using some of the object oriented aspects of TypeScript.

It allows us to code defensively and ensure that we have the exact data properties that we need for the project. Otherwise it will throw a Type error and you can adjust the type definition accordingly.

Get the Data using Angular HTTP Object

We now want to implement a method that retrieves all beers from our Beers API.

Open up beer.service.ts and change it to the following.

 import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import { Beer } from './beer';
import "rxjs/Rx";
import { Subject } from 'rxjs/Subject';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class BeerService {
  private _baseUrl: string = "/api/v2/"
  private _allBeersUrl: string = this._baseUrl + "beers?glasswareId=1&withBreweries=Y"
  private _randomBeerUrl: string = this._baseUrl + "beer/random?hasLabels=Y&withBreweries=Y";
  private _beerByIdUrl: string = this._baseUrl + "beer/oeGSxs&key="
  private _searchUrl: string = this._baseUrl + 'search?q='
  beerAnnouncedSource = new Subject();

  result: any;
  apiKey: string = "&key=yourkeyhere";
  beers: Array;
  beerAnnounced$ = this.beerAnnouncedSource.asObservable();
  beer: Beer;
  searchResults: any;


  constructor(private _http: Http) { }

  getBeers() {

    if (!this.result) {
      return this.result = this._http.get(this._allBeersUrl + this.apiKey)
        .map((res: Response) => res.json())
        .publishReplay(1)
        .refCount()
        .catch(this.handleError);
    }
    return this.result;
  }
  
  handleError(error: Response | any) {

    let errMsg: string;
    if (error instanceof Response) {
      const body = error.json() || '';
      const err = body.errorMessage || JSON.stringify(body);
      errMsg = ` ${error.statusText || ''} ${err}`;
    }
    else {
      errMsg = error.message ? error.message : error.toString();
    }
    return Observable.throw(errMsg);
  }

So we now have a getBeers() method and a handleError method for our API calls.

In the first condition we check if the this.result variable already has a value:

 if (!this.result)

This is an important step because our service shares permanent data throughout our application so if the application has already been loaded then there is no point in making the getBeers() API call again as the data is already cached in the our browser/service to be used.

We then take the _http variable of type HTTP that was created in our constructor:

 constructor(private _http: Http) { } 

And then invoke the get method using our beer API url details as the parameters for it.

return this.result = this._http.get(this._allBeersUrl + this.apiKey)

The next line:

.map((res: Response) => res.json())

Takes the response from the Beer API server and maps it into a JSON array for us.

.publishReplay(1)

This will allow the data to be reactive. We will come back to this.

.refCount(1)

This line has a similar function. It allows the Observable returned by the HTTP get request to keep its connection open, this will allow the data source to be reactive.

We also will catch any errors here:

catch(this.handleError)

This line will catch any error from our request and run the this.handleError function.

All you really need to know about this function is that it will filter down through the error message to the point where it will usually be the most informative. Usually, a detailed HTTP code error or something associated with that code.

Observables Ready for the UI

If you noticed some of the variables declared above the constructor, at the top you should see:

 beerAnnouncedSource = new Subject();
 beerAnnounced$ = this.beerAnnouncedSource.asObservable();

These are both core Rxjs features that will allow our data to be reactive. We will come back to these but for now just think of them as reactive variables that we will using later on in our home.component.ts .

Retrieving the Angular Data

This is the basic version of our service set up with the functionality to retrieve an initial list of beers. It gives a good overview of how our service will share data. However, this on its own will not do anything without using it into one of your UI components.

We will be doing this in part 3, for now think of this service as a piece of functionality that will share data throughout the application, the data will be made reactive by using Rxjs Subjects and Observables which are declared at the top of the file.

Proudly published with Gatsby