Angular 2 Services and Http Client

Tags: Angular

Let's talk about Angular services! Too many examples I've read online have the component talk directly to the data source. This is not considered a best practice. Your components should delegate data calls to a service. So let's look closer at that. The full source for this post is available on GitHub:

git clone https://github.com/jamesstill/Angular2-Widgets.git

In Angular 2 a service is a wrapper that manages data calls to a data source. The most common scenario is a service for a RESTful API. Services are injectable, meaning they are created as an export class in order to inject them into one or more components that will use them. So they typically have an @Injectable decorator:

import { Injectable } from '@angular/core';

@Injectable()
export class WidgetService {
    
}

A component references the service with an import statement and then tells Angular to inject an instance of the service into the component class:

import { WidgetService } from './shared/widget.service';
// other import statements...

@Component({
    moduleId: module.id,
    selector: 'widget-list',
    templateUrl: 'widget-list.component.html'
})
export class WidgetListComponent implements OnInit {
    // DI for the service
    constructor(private service: WidgetService) { }

    getWidgets() {
        // TODO: Use the service to fetch all widgets
    }
}

It's worth mentioning at this point that Angular is taking care of instantiating and injecting the service into our component constructor for us. We don't need a local variable and we don't need to new it up. But we do need to register it as a provider in the NgModule class (app.module.ts):

@NgModule({
  imports:      
  [ 
    BrowserModule, 
    FormsModule, 
    HttpModule, 
    AppRouting 
  ],
  declarations: [ 
    AppComponent
  ],
  providers:    [ WidgetService ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

I'll talk more about how the component consumes the service in a minute. First, I'd like to go back to the service itself. Just as we told Angular to inject the service into the component for web API scenarios we need to inject Angular's Http client into the service constructor. In addition to the Http client I'm injecting a local config class that defines my api:

@Injectable()
export class MyConfiguration {

    private server: string = "http://angularwidgetsapi.azurewebsites.net";
    private api: string = "api/v1/widget";
    public Url = this.server + "/" + this.api;
}

@Injectable()
export class WidgetService {   
    
    private url: string;
    private headers: Headers;
   
    constructor(private http: Http, private configuration: MyConfiguration) { 
        this.url = configuration.Url;
        this.headers = new Headers();
        this.headers.append('Content-Type', 'application/json');
        this.headers.append('Accept', 'application/json');
    }

    // implementation goes here
}

As of this writing that url goes to a real api hosted on my Azure account. It has an in-memory database that holds 4 widgets. The database is reset back to those 4 widgets when the Azure web app is recycled. The api is also configured to enable Cross-Origin Resource Sharing (CORS) to get around the origin policy.

Notice that the service instantiates an Angular Headers class and sets the content type for JSON data. With the Http client injected the service will now be able to make asycronous calls using XMLHttpRequest. Let's get all widgets:

getWidgets() : Observable<Widget[]> {
    return this.http.get(this.url)
        .map(response => <Widget[]> response.json())
        .catch(this.handleError);
}

Here we're calling the api via the http client get function. The Angular team decided to incorporate the Reactive Extensions (RxJs) library in its Http client in order to use the asynchronous observable pattern. I'm not going to go into that too much. I'll just say that all of the calls that the service makes to the api are going to be async and our data is wrapped in an Observable. When the response is received (assuming it's in the 200 range) the map function is called and the stream is mapped to an observable Widget array. If there's an error then the catch block will call a function to log it. (In my code sample it just writes out to the console.)

For those of you coming from JQuery this is like the $.getJSON() function. But Angular's syntax really shines when you want to post to the api:

saveWidget(item: Widget) : Observable<Widget> {
    let body = JSON.stringify(item);
    let options = new RequestOptions({ headers: this.headers });
    return this.http.post(this.url, body, options)
        .map(this.extractData)
        .catch(this.handleError);
}

Compare this to the old JQuery way of doing it:

var request = $.ajax({
    type: "POST",
    url: url
});

request.done(function (response) {
    // mapping from response
    self.widgets.push(widget);
});

request.fail(function (jqXHR, textStatus) {
    $("#message").text("Post failed! " + textStatus);
});

Let's turn back to the component. When it calls the service getWidgets() function it's getting back an Observable. In order to get my widgets I need to subscribe to the "underlying data stream" that the observable emits:

getWidgets() {
    this.service.getWidgets()
        .subscribe(
            (widgets: Widget[]) => this.widgets = widgets,
            (error: any) => this.errorMessage = <any>error);
}

This loads my widget array into a local class variable (or displays an error message if something went wrong). The HTML template can now iterate over the widget array and display them in a table or another structure:

<table class="table" *ngIf="widgets && widgets.length">
   <thead>
       <tr>
           <td class="bold">ID</td>
           <td class="bold">Shape</td>
           <td class="bold">Name</td>
       </tr>
   </thead>
   <tbody>
       <tr *ngFor='let widget of widgets'>
           <td>
               {{ widget.ID }}
           <td>
               {{ widget.Shape }}
           </td>
           <td>
               {{ widget.Name }}
           </td>
       </tr>
   </tbody>
</table>

I have a fully-functional CRUD solution for widgets on my GitHub page. Feel free to clone it and check it out.