30 December 2016

Angular 2 Step by Step Guide: How to consume a REST web service via angular service?

Angular 2 has a modular development architecture where each module has its own set of components, templates and routes. Service is more at an application level and can be shared by multiple components or modules. Angular 2 has a concept of injector whereby you don’t need to create the instance of the service yourself but whenever you need to consume service you can request Angular to provide you with an instance. In this blog we are going to consume an external web service which return list of countries and create an angular service to use that web service to provide list of countries to the dropdown in component.

Think of angular service as nothing special but an ES6 class that exports some methods to be consumed by components. First lets clone/download an Angular 2 seed project from Angular’s github repo ( https://github.com/angular/angular2-seed ) and navigate to angular2-seed folder and run npm install. This will install all necessary modules to run the application.
Open your project in visual studio code which is a free code editor.

In src folder let’s create a services folder and add a file called countrylist.service.ts. In this file add a class called CountryListService . This is a bare bone class that will provide you with a method to get list of countries so lets add a method to it called getCountries().
 
class CountryListService {
getCountries(){
//This method will return list of countries.
}
}

For service to be available to other components we have to export the class and add @Injectable() decorator to class to allow Angular to inject objects in it when required. Think of decorates as an annotations used in C# or java. It provide some metadata to angular to function it properly. To use @Injectable you have to import it from '@angular/core'. At this point you have a class that looks like.
 
import { Injectable } from '@angular/core';
@Injectable()
export class CountryListService {
getCountries(){
//This method will return list of countries.
}
}

Now we are ready to write actual implementation of getCountries(). The REST web service we are going to use is ( https://restcountries.eu/rest/v1/all ). It returns a lot of data but we are only interested in name of countries. We need an http object to call this web service so we need to import Http module from '@angular/http’. Now you can declare an http object in the class and create a new instance when required but Angular provides an alternative where you declare a private variable in constructor of the type you need and it will create an instance when you need it. This is done via dependency injection without you needing to create an object explicitly.

 
import { Injectable } from '@angular/core';
import {Http, Response, Headers, RequestOptions} from '@angular/http';
@Injectable()
export class CountryListService {
constructor(private http:Http) {
     }
getCountries(){
//This method will return list of countries. We can use http variable from constructor to call get method.
}
}

Now we implement getCountries() method. We use http.get method which returns an Observable . Observable provides an async behaviour. Observable comes from RxJs so you have to import it. Once you get a response you have to map it or catch any error. So we implement two functions to deal with either response or error. getCountries() will return Observable of type any[] array. We also need to import map and catch operators from RxJs. In this implementation all we are doing is calling the REST web service and once response comes back and if it is 200 OK we map the response to get the json result out of it and return the result or throw an Observable exception if there is any error calling the web service.


 
getCountries():Observable{
         //You can provide additional header options to get which is not required here.
         //let headers = new Headers({'content-type':'application/json'});
         //let options = new RequestOptions({headers:headers});
        return this.http.get("https://restcountries.eu/rest/v1/all")
                    .map(this.extactData)
                    .catch(this.handleError);
     }

     private extactData(resp:Response){
         let body = resp.json();
         console.log(body);

         return  body ;
     }

     private handleError(error:any){
         console.log(error);
       return Observable.throw(error.statusText);
     }


Now we have fully implemented our service lets make it available to the components. Open app.module.ts and add it to providers array providers: [ CountryListService,... ]. You need to import the service from services folder. This makes Angular aware that whenever any component in AppModule asks for object of type CountryListService where it can create one from. Angular maintains a single instance of this service per module using injector.

 
import {CountryListService} from './services/countrylist.service'
providers: [
    CountryListService,
    GithubService
  ],
  


Now that our service is implemented and ready to be used by components in AppModule lets go to home folder and open home.component.ts . Let import our service and observable first. We use the same principal we used in service to inject service instance via private constructor variable and create a variable of type array to hold list of countries.
 
import {CountryListService} from '../services/countrylist.service'
import {Observable } from 'rxjs/Rx'


countries:any[]=[]; // declared inside the HomeComponent class

 constructor(private countryListService : CountryListService){
  
 }  


Where should you call the service you just written to populate countries array ? It depends on at what point you need the data. If you need the data when your form loads you can implement OnInit from Angular and override ngOnInit method and call the web service there. So lets do that. You need to import OnInit from @angular/core. At this point class signature will look like export class HomeComponent implements OnInit. getCountries() return an Observable so you have to subscribe to it to get data out of it. Here we are using lambda like syntax to subscribe to response or error. Once data is returned we are logging it to console and populating the array with only name field. Hover over subscribe to understand the syntax properly. If there is an error we are just simply logging it to console. You may want to do more with error later.

 
ngOnInit() {
      this.countryListService.getCountries().subscribe(
     data=>{console.log(data); 
      for (var i=data.length;i--;) {
        console.log('returned : ' + data[i].name)
          this.countries[i]=data[i].name;  
        }
      },
     err=> console.log(err)
   )
   }


Now we are ready to display this data in home.component.html. I have added a dropdown for country. We are using *ngFor directive to loop through each country in the array and add it as an option.

 
  <select class="form-control" #country
   name="country">
      <option value="default">Select country...</option>
      <option *ngFor="let cnt of countries">
          {{ cnt }}
      </option>
  </select>


Now to run the application from command prompt run npm start and navigate to browser http://localhost:3000/ . You will be able to see list of countries in a dropdown.


You can use Google chrome’s developer tools using F12 to view the console log. That’s it. There are many moving parts but once you understand how it all fits together it becomes easy to implement service. You can extend on this service by implementing OnDestroy and unsubscribe from the Observable.