Reputation: 2945
How do I store a local variable or cache it so that the results of an API call can be shared throughout the application?
I have the following Angular 6 module which basically creates a mentions directive to be able to do @mentions popular in many apps:
@NgModule({
imports: [
CommonModule
],
exports: [
MentionDirective,
MentionListComponent
],
entryComponents: [
MentionListComponent
],
declarations: [
MentionDirective,
MentionListComponent
]
})
export class MentionModule {
static forRoot(): ModuleWithProviders {
return {
ngModule: MentionModule
};
}
}
This is the actual directive:
export class MentionDirective implements OnInit, OnChanges {
items: any[];
constructor(
public autocompletePhrasesService: AutocompletePhrasesService
) { }
ngOnInit() {
this.autocompletePhrasesService.getAll()
.pipe(first())
.subscribe(
result => {
this.items = result;
},
() => { });
The directive calls a function in my core module which retrieves the data:
export class AutocompletePhrasesService {
public autoCompletePhrases: string[];
constructor(private http: HttpClient) { }
getAll(): Observable<string[]> {
return this.http.get<string[]>(this.baseUrl + '/getall');
}
My problem is that this directive may have 20-30 instances on a single page and the API gets called for every instance of the directive. How can I change my code so that the API gets called just once per application instance?
The data does not change often from the API.
I have tried storing the results of the service in the public autoCompletePhrases variable, and only calling that if it is empty, but that hasn't worked
Upvotes: 2
Views: 815
Reputation: 27289
Here is some AngularJS/TypeScript code to demonstrate the logic of how to handle this. Apologies but I don't have time to convert it to Angular. I do believe there are also packages out there which handle caching in a more complete fashion but this is the manual way to do it :)
private printerList: SelectItem[] = [];
private printerPromises: IDeferred<SelectItem[]>[] = [];
private printersLoading: boolean = false;
private printersLoaded: boolean = false;
public getPDFPrinterList(): ng.IPromise<SelectItem[]> {
let defer: IDeferred<SelectItem[]> = this.$q.defer<SelectItem[]>();
this.printerPromises.push(defer);
if (this.printersLoading) {
return defer.promise;
}
if (this.printersLoaded) {
for (let i = 0; i < this.printerPromises.length; i++) {
this.printerPromises[i].resolve(this.printerList);
}
this.printerPromises = [];
} else {
this.printersLoading = true;
this.$http.get<SelectItem[]>(this.printerListUrl, { cache: false }).then((response: ng.IHttpResponse<SelectItem[]>) => {
if (response.data) {
this.printerList = response.data;
for (let i = 0; i < this.printerPromises.length; i++) {
this.printerPromises[i].resolve(this.printerList);
}
this.printerPromises = [];
this.printersLoaded = true;
} else {
console.log('Server Error Obtaining Printer List');
for (let i = 0; i < this.printerPromises.length; i++) {
this.printerPromises[i].reject('Server Error');
}
this.printerPromises = [];
}
this.printersLoading = false;
}, (response: ng.IHttpResponse<any>) => {
if (response.data) {
console.log(response.data.Message ? response.data.Message : response.data);
}
for (let i = 0; i < this.printerPromises.length; i++) {
this.printerPromises[i].reject('Server Error');
}
this.printerPromises = [];
});
}
return defer.promise;
}
Upvotes: 1
Reputation: 57981
You want some like "cache". Its easy in service make some like
data:any;
getData()
{
if (!this.data)
return this.httpClient.get(....).pipe(
tap(result=>{
this.data=result
}))
return of(this.data)
}
When you subscribe to "getData" can happen two things: this.data has value, then return an observable with the data. Or this data has no value, and return a get, and store the result in data -tap is executed after subscribe-. So you ALLWAYS subscribe to "getData", but the service only call to API if has not data (NOTE: can happens that there're more that one call at time and the call to API more than one time)
Upvotes: 1