Enough of building an app with five beers in a hard-coded dataset! Let's fetch a larger dataset from our server using one of Angular's built-in services called HttpModule
. We will use Angular's dependency injection (DI) to provide the service to the beerList.component
controller.
Our new dataset is now a list of 11 beers stored in JSON format in the beers/beers.json
file in your project.
This file is available from the server at the URL http://127.0.0.1:8000/beers/beers.json
beers/beers.json
:
[
...
{
"alcohol": 6.8,
"description": "A reddish-brown abbey ale brewed with dark malts. The secondary fermentation gives a fruity aroma and a unique spicy character with a distinctive aftertaste. Secondary fermentation in the bottle.",
"id": "AffligemDubbel",
"img": "beers/img/AffligemDubbel.jpg",
"name": "Affligem Dubbel"
},
...
]
We'll use Angular's HttpModule service in our controller to make an HTTP request to your web server to fetch the data in the beers/beers.json
file.
HttpModule
is just one of several built-in modules that handle common operations in web apps.
Angular injects these services for you where you need them.
Dependency injection helps to make your web apps both well-structured (e.g., separate components for presentation, data, and control) and loosely coupled (dependencies between components are not resolved by the components themselves, but by the DI subsystem).
HttpModule
makes an HTTP GET request to our web server, asking for beers/beers.json
(the url is relative to our index.html
file). The server responds by providing the data in the JSON file. (The response might just as well have been dynamically generated by a backend server. To the browser and our app they both look the same. For the sake of simplicity we used a JSON file in this tutorial.)
The HttpModule service returns a Promise with a success method. We call this method to handle the asynchronous response and assign the beer data to the scope controlled by this controller, as a model called beers
. Notice that Angular detected the JSON response and parsed it for us!
To use a service in Angular, you simply declare the names of the dependencies you need as arguments to the controller's constructor function, as follows:
app/app.module.ts
:
import {NgModule} from "@angular/core";
import {BrowserModule} from "@angular/platform-browser";
import {FormsModule} from "@angular/forms";
import { HttpModule, JsonpModule } from '@angular/http';
import {BeerList} from "./beerlist/beerList.component";
import {FilterArrayPipe} from "./pipes/filter-array-pipe";
import {OrderByPipe} from "./pipes/orderby-pipe";
@NgModule({
imports: [
BrowserModule,
FormsModule,
JsonpModule,
HttpModule
],
declarations: [
BeerList,
FilterArrayPipe,
OrderByPipe
],
bootstrap: [BeerList]
})
export class AppModule {
}
app/beers.service.ts
:
import {Injectable} from "@angular/core";
import {Http, Response} from "@angular/http";
import "rxjs/add/operator/toPromise";
@Injectable()
export class BeerService {
// URL to web API
private beerUrl = 'beers/beers.json';
constructor(private http: Http) {
}
getBeers(): Promise<any[]> {
return this.http.get(this.beerUrl)
.toPromise()
.then(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
let body = res.json();
return body || [];
}
private handleError(error: any) {
// In a real world app, we might use a remote logging infrastructure
// We'd also dig deeper into the error to get a better message
let errMsg = (error.message) ? error.message : error.status ? `${error.status} - ${error.statusText}` : 'Server error';
console.error(errMsg); // log to console instead
return Promise.reject(errMsg);
}
}
app/beerlist/beerList.component.ts
:
import {Component} from '@angular/core';
import {FilterArrayPipe} from '../pipes/filter-array-pipe';
import {OrderByPipe} from '../pipes/orderby-pipe';
import { BeerService } from '../beers.service';
@Component({
selector: 'beer-list',
templateUrl: './app/beerlist/beerList.html',
pipes: [FilterArrayPipe, OrderByPipe],
providers: [BeerService]
})
export class BeerList {
orderProp = 'alcohol';
beers = [];
mode = 'Promise';
constructor (private beerService: BeerService) {}
ngOnInit() { this.getBeers(); }
getBeers() {
this.beerService.getBeers()
.then(
beers => {this.beers = beers; console.log(beers)},
error => this.errorMessage = <any>error);
}
}
Angular's dependency injector provides services to your controller when the controller is being constructed. The dependency injector also takes care of creating any transitive dependencies the service may have (services often depend upon other services).
At the bottom of app/beerlist/beerList.html
, add a <pre>{{beers | json}}</pre>
binding to see the list of beers displayed in json format.
At the top of app/beerlist/beerList.html
, add a
<div class="alert alert-danger" role="alert" *ngIf="errorMessage">
<strong>Oh snap!</strong> {{errorMessage}}.
</div>
to display an error message, you can test if by modifying the beerUrl
in app/beers.service.ts
.
In the BeerList component, pre-process the http response by limiting the number of beers to the first 5 in the list.
Use the following code in the getBeers
callback:
getBeers() {
this.beerService.getBeers()
.then(
beers => { this.beers = beers.splice(0, 5); },
error => this.errorMessage = <any>error);
}
Now that you have learned how easy it is to use Angular services (thanks to Angular's dependency injection), go to step 6, where you will add some thumbnail images of beers and some links.