Precondition
I have the following situation:
- on the backend I have an endpoint, which delivers data. Data is changed very-very seldom.
- on the frontend I have several components, which need the data from p.1 right after the loading.
Question
How to avoid multiple requests to the backend ( = how to share the result of the first request to all the other callings of a service method).
Source code - service
@Injectable()
export class ConfigurationService {
private ConfigurationUrl: string = 'api/configurations/';
private httpHeaders: HttpHeaders;
// ...
constructor(private httpClient: HttpClient) {
}
// ...
loadCommonSettings(): Observable<CommonSettings> {
return this.httpClient.get<CommonSettings>(this.ConfigurationUrl "common");
}
// ...
}
Source code - components
export class HomeComponent {
constructor(public dialog: MatDialog, private configurationService: ConfigurationService, private _snackBar: MatSnackBar) {
}
isNewDisabled: boolean = true;
commonSettings: CommonSettings;
ngOnInit() {
this.loadCommonSettings();
}
//...
loadCommonSettings() {
this.configurationService.loadCommonSettings().subscribe(
res => {
this.isNewDisabled = false;
this.commonSettings = res;
},
err => {
this.openErrorDialog("Error", "Error on data loading");
}
);
}
//...
}
Problem Each calling of loadCommonSettings service function fires a new request to the service.
Supposes
- I thought to store data in the service as in the cache, but anyway I have 2 almost simultaneous calls of a service method.
- Maybe Subjects may help in this situation, but I'm completely not sure about it (because it seems, that Observables are also absolutely Ok for such calls).
CodePudding user response:
Caching is a senseful choice here as you already thought.
In the following an example for your service. You can also use a BehaviorSubject
instead of the ReplaySubject
and initialize it with undefined.
@Injectable()
export class ConfigurationService {
private ConfigurationUrl: string = 'api/configurations/';
private httpHeaders: HttpHeaders;
private commonSettings$: Subject<string>;
// ...
constructor(private httpClient: HttpClient) {
}
// ...
loadCommonSettings(): Observable<string> {
if (!this.commonSettings$) {
this.commonSettings$ = new ReplaySubject<string>(1);
this.httpClient.get<string>(this.ConfigurationUrl "common")
.subscribe(data => this.commonSettings$.next(data));
}
return this.commonSettings$;
}
// ...
Here only the fist call to the service is starting a http-call. And all calls including the first one are only returning the subject.
CodePudding user response:
Below are the solution without using Subjects
Option 1: Using ShareReplay
loadCommonSettings(): Observable<CommonSettings> {
return this.httpClient.get<CommonSettings>(this.ConfigurationUrl "common").pipe(
take(1),
shareReplay(1)
);
}
Option 2 : Using local-Variable to hold result
// declare variable to hold cache
private cachedSetting: CommonSettings = null;
loadCommonSettings(): Observable<CommonSettings> {
// if cachedSetting already available then return same
if(cachedSetting){
return of(this.cachedSetting);
}
return this.httpClient.get<CommonSettings>(this.ConfigurationUrl "common").pipe(
tap(response => this.cachedSetting = response)
);
}
Imports used:
import { shareReplay, take, tap } from 'rxjs/operators';
import { of } from 'rxjs';