By using Http, we call a method that does a network call and returns an http observable:
getCustomer() {
return this.http.get('/someUrl').map(res => res.json());
}
If we take this observable and add multiple subscribers to it:
let network$ = getCustomer();
let subscriber1 = network$.subscribe(...);
let subscriber2 = network$.subscribe(...);
What we want to do, is ensure that this does not cause multiple network requests.
This might seem like an unusual scenario, but its actually quite common: for example if the caller subscribes to the observable to display an error message, and passes it to the template using the async pipe, we already have two subscribers.
What is the correct way of doing that in RxJs 5?
Namely, this seems to work fine:
getCustomer() {
return this.http.get('/someUrl').map(res => res.json()).share();
}
But is this the idiomatic way of doing this in RxJs 5, or should we do something else instead?
Note : As per Angular 5 new HttpClient
, the .map(res => res.json())
part in all examples is now useless, as JSON result is now assumed by default.
Cache the data and if available cached, return this otherwise make the HTTP request.
import {Injectable} from '@angular/core';
import {Http, Headers} from '@angular/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/observable/of'; //proper way to import the 'of' operator
import 'rxjs/add/operator/share';
import 'rxjs/add/operator/map';
import {Data} from './data';
@Injectable()
export class DataService {
private url: string = 'https://cors-test.appspot.com/test';
private data: Data;
private observable: Observable<any>;
constructor(private http: Http) {}
getData() {
if(this.data) {
// if `data` is available just return it as `Observable`
return Observable.of(this.data);
} else if(this.observable) {
// if `this.observable` is set then the request is in progress
// return the `Observable` for the ongoing request
return this.observable;
} else {
// example header (not necessary)
let headers = new Headers();
headers.append('Content-Type', 'application/json');
// create the request, store the `Observable` for subsequent subscribers
this.observable = this.http.get(this.url, {
headers: headers
})
.map(response => {
// when the cached data is available we don't need the `Observable` reference anymore
this.observable = null;
if(response.status == 400) {
return "FAILURE";
} else if(response.status == 200) {
this.data = new Data(response.json());
return this.data;
}
// make it shared so more than one subscriber can get the result
})
.share();
return this.observable;
}
}
}
This article https://blog.thoughtram.io/angular/2018/03/05/advanced-caching-with-rxjs.html is a great explanation how to cache with shareReplay
.