REST or HTTP based services are the primary requirements of single page applications to retrieve the data and gel into the web application. Angular offers its inbuilt http client service which wraps the major functions for requesting the data from server where REST service is hosted.
Angular provides HttpClient for this purpose which is packaged under @angular/common/http. Before we can use HttpClient, we have to import HttpClientModule in our main application module.
import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {HttpClientModule} from '@angular/common/http';
@NgModule({
imports: [
BrowserModule,
HttpClientModule,
],
})
export class MyAppModule {}
HttpClient is best consumed when used with Observables. You must be asking a question - What is an Observable here? An Observable is simply a collection of future events which arrives asynchronously over time.
RxJS
An Observable is a class provided by RxJs. To learn more about RxJS, please visit http://reactivex.io/rxjs/
Observables are pretty much similar to promise or callbacks but the main advantage is they are lazy loaded. Observables will not be called till the time any subscriber invoked on it. Feel free to play around at RxMarbles website http://rxmarbles.com to get to know how values are observed over time.
Building our example app
Let us start with an example. Here we are building a simple survey which will ask a question and user can choose from one of the four option. We will get our survey data from our Http service as JSON. Full working example is here - StackBlitz - Angular Http Service and Observables
Create a survey interface
export interface ISurvey{
question:string;
choices: string[];
}
Our Survey Data
Let's add couple of questions in our json survey data which we will request from our http service. You can add more later.
[
{
"question": "What is your favourite programming language?",
"choices": ["Swift","Python","Objective-C","Ruby"]
},
{
"question": "What is your major learning source?",
"choices": ["Books","Online Tutorials","Online Bootcamp courses"]
}
]
Our Survey Service
We will now add our survey service class which will provide data interaction operations to our survey class. Now, to make survey service injectable we'll import Injectable from '@angular/core' and decorate our component with @Injectable().
@Injectable()
export class SurveyService {
..
}
Injecting HttpClient into Survey service
Add http functionality by importing HttpClient from '@angular/common/http' and inject the HttpClient in our constructor:
import {HttpClient, HttpErrorResponse } from '@angular/common/http'
export class SurveyService {
constructor(private _http:HttpClient){
..
}
Adding Observable method in Survey service
As discussed previously, we will add Rxjs Observables to work with Http services since we have values that will be received in some point of time. Observables works best in these scenarios.
import {Observable} from 'rxjs/Observable'
Add the following, these are required for Observables operations:
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';
Now add an Observabbe method which will get the survey data using HttpClient:
getSurveyQuestion(): Observable<ISurvey[]>{
return this._http
.get<ISurvey[]>('./src/survey.json')
.do(data =>console.log('All : ' + JSON.stringify(data)))
.catch(this.handleError);
}
Look that we are returning an Observable of ISurvey[] array. Observable are lazy, an Observable method will only be invoked when a subscription is called that.
Injecting our Survey Service:
Now, as we have our service ready, we will inject it in our Survey class constructor:
import {ISurvey} from './isurvey'
import {SurveyService} from './survey.service';
export class Survey {
MySurvey : ISurvey[] = [];
constructor(private _surveyService: SurveyService){
...
}
}
Calling our observable method from SurveyService
We can not call that in our constructor. Why?
Just for the simple reason that we can not delay the construction of our component till the time we get our json data from the network.
So, where we will call this method?
Angular provides us with Lifecycle hooks. Read more here..
Services and Dependency Injection
One of the lifecycle hook is ngOnInit(). After our component is constructed, ngOnInit() lifecycle hook is called. So, we will implement it and call our RESTful method within this lifecycle hook.
export class Survey implements OnInit{
MySurvey : ISurvey[] = [];
constructor(private _surveyService: SurveyService){
}
ngOnInit(){
this._surveyService.getSurveyQuestion().subscribe(data => {
console.log(data);
this.MySurvey = data;
}, err => {
console.log(err);
});
}
}
Finally, here is our template which will be rendered as soon as data gets populated in our ISurvey[] array.
Component({
selector: 'survey',
template: `<div>
<h2>Question : </h2>
<div *ngIf="MySurvey && MySurvey.length">
<div *ngFor="let survey of MySurvey; let idx = index"><br/>
<div></div>
<div *ngFor="let choice of survey.choices">
<input type="radio" name="radioGroup"/>
</div>
</div>
</div>
</div>
`
})
export class Survey implements OnInit{
...
}
The RxJS Retry operator
If your want to retry your http requests on case you receives error in your http response, then there is a retry operator (provided by RxJs) that you can use to resubscribe to the observable to re-invoke it for a number of times.
This following code will retry 3 times as http service continues to receive any http error.
import 'rxjs/add/operator/retry';
http
.get('/api/questions')
.retry(3)
.subscribe(...);
You can go through the advance Http configuration and functions from the Angular's official documentation: https://angular.io/guide/http
Full working example here
https://stackblitz.com/edit/angular-http-service-and-observables?embed=1