I would like to create a service method which at first execution will fetch a dataset from an URL, cache the dataset, and then share the same instance of the dataset with the components which subsequently call the service method.
I have started creating two solutions. The problem with both solutions is that in the first milliseconds after loading the Angular app, they fetch the dataset from the URL multiple times, and/or they throw some error messages to the console, but after the first or second navigation between the two components, the data seems to be shared correctly, and there is no more roundtrip to the URL.
Approach #1
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, of } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class FirstService {
private url = 'http://somedummydomain.com/api/entries';
private data: any;
private observable: Observable<any>;
constructor(private http: HttpClient) {}
getData() : Observable<any> {
if (this.data) {
return of(this.data);
} else if (this.observable) {
return this.observable;
} else {
this.observable = this.http.get(this.url);
this.observable.subscribe((data) => {
this.data = data;
this.observable = null;
});
return this.observable;
}
}
}
Approach #2
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http';
@Injectable({
providedIn: 'root',
})
export class SecondService {
private sharedData: BehaviorSubject<any> = new BehaviorSubject<any>(null);
private url = 'http://somedummydomain.com/api/entries';
constructor(private http: HttpClient) {
this.http.get(this.url).subscribe((data) => {
this.addData(data);
});
}
private addData(data: any) {
this.sharedData.next(data);
}
getData(): Observable<any> {
return this.sharedData.asObservable();
}
}
Lib versions
Thanks!
For completeness, here is the reactive style of solving your requirement (sharing data across subscribers):
@Injectable({
providedIn: 'root',
})
export class SecondService {
private url = 'http://somedummydomain.com/api/entries';
private data$: Observable<any>;
constructor(private http: HttpClient) {
this.data$ = this.http.get(this.url).pipe(
shareReplay(1)
);
}
getData(): Observable<any> {
return this.data$;
}
}
The shareReplay
operator will do two things:
this.data !== undefined
branch.share
instead.If you need data
as a property, you can set it with a tap
operator like this:
this.data$ = this.http.get(this.url).pipe(
tap(data => this.data = data),
shareReplay(1)
);