One can often encapsulate the data access for our application into a data service that can be used by any component or other service that needs it. In the sample application product management instead of using the hard-coded list of products one can send an Http request to get the products from a backend web server.
Angular provides an Http service that allows users to communicate with a backend web server. Using the familiar Http request and response protocol. For example, one can call a get method of the Http service, which in turn sends a get request to the web server. The web server response is returned from the Http service to our product data service as an observable as shown in the figure below.
The snippet code below is the product service built in sample application modified to retrieve the products using Angular’s Http service.
Product.service.ts
[c]
...
import { Http } from '@angular/http';
@Injectable()
export class ProductService {
private _productUrl = 'www.myWebService.com/api/products';
constructor(private _http: Http) { }
getProducts() {
return this._http.get(this._productUrl);
}
}
[/c]
Here in the code, a
URL is specified to the products on the web server. The Url defines where to send the Http requests, Note that the Url in the code is shown for illustration purposes only and is a real URL.
Next a
constructor is added, the constructor is used to inject the dependencies. In this case, Angular’s Http service is required so injected the dependencies, the syntax creates a
private_http variable and assigns the injected service instance to that variable, and since the variable is strongly typed to Http, imported the Http from the
@angular/http package .
In some cases the injectable decorator was optional but in this case, the product service has an injected dependency, the injectable decorator is required. One can inject a service in as a dependency, but need to register that service's provider with
Angular's injector.
The snippet code below is the app module built in sample application.
app.module.ts
[c]
...
import { HttpModule } from '@angular/http';
@NgModule({
imports: [
BrowserModule,
FormsModule,
HttpModule ],
declarations: [
AppComonent,
ProductListComponent,
ProductFilterPipe,
StarComponent ],
bootstrap: [AppComponent ]
})
export class AppModule { }
[/c]
The Http service provider registration is done in the Http module so include the
@angular/http package as shown in the code above. In order to include the features of the external package in the application add the imports array of the application’s Angular module,
AppModule.
The declarations array is used for declaring components, directives and pipes that belong to the module, the imports array is for pulling in the external modules, so the import array in the code is used to pull the external HttpModule.
Go back to the product service code in the sample application and add the observable response as shown in the code below.
[c]
...
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
@Injectable()
export class ProductService {
private _productUrl = 'www.myWebService.com/api/products';
constructor(private _http: Http) { }
getProducts(): Observable<Response> {
return this._http.get(this._productUrl)
.map((response: Response) => <IProduct[]>response.json());
}
}
[/c]
In the code the
getProducts is used to inject Http service instance and the get method is called, passing in the desired URL. The Http service then sends a get request using the specified URL. Since using strong typing, a function return value should be given. The getProducts method returns an observable of Response.
Observables take advantage of generics to define the type of data it is observing in the observable sequence. In this case, it's the Http Response. It's important to note that Http calls are single asynch operations, meaning that the observable sequence contains only one element, which is the Http Response object. And one need to import Response from the
@angular/http package and observable from rxjs/Observable.
The components, such as the product list component, are expecting to receive a list of products, not an Http response. So one need to translate the Response object to an array of products which can be done using the
map operator.
The
map operator takes the raw Http Response object returned from the Http get method and translates it into an array of products. The argument to the map method is an arrow function that transforms the response to a
JSON object. Use a casting operator to cast the JSON object to an array of products. So now the
getProducts returns an observable of
IProduct array, which is much more useful for the components. Note that depending on the server, the JSON result may be wrapped in another property, such as a data property. If that is the case, use
response.json.data. To use the map operator, one need to load it using an import statement. Using the import statement as shown in the code is a bit unusual. It tells the module loader to load the library but actually doesn't import anything. When a library loaded, its JavaScript is executed, and for this particular library, executing the JavaScript loads the map operator. So this particular syntax imports the module for its side-effects only, without actually importing any of its features.