i have a problem in making my caching more simpler. I think there is a better way to do this. My problem is that i have to do this "caching" codes in every get() function which is results in longer codes. Anyone help on how to do this the best way? Thanks. Here's my codes below. What i did in my codes is i do the get() function in my news.service.ts to get data from the http and i subscribe it in my news-list.
news.service.ts
getAllNews() {
if(this.newslist != null) {
return Observable.of(this.newslist);
}
else {
return this.httpClient
.get('http://sample.com/news')
.map((response => response))
.do(newslist => this.newslist = newslist)
.catch(e => {
if (e.status === 401) {
return Observable.throw('Unauthorized');
}
});
}
}
news-list.service.ts
this.subscription = this.newsService.getAllNews()
.subscribe(
(data:any) => {
console.log(data);
this.newslists = data.data.data;
},
error => {
this.authService.logout()
this.router.navigate(['signin']);
});
}
If you meant to have something generic, that you can use for different API calls or services, then you could do something like this:
import { Injectable } from '@angular/core';
import { HttpClient } from "@angular/common/http";
import { Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';
class CacheItem<T> {
url: string;
timestampCached: number;
data: T;
}
@Injectable({
providedIn: 'root'
})
export class MyCachedHttpClient {
cache: CacheItem<any>[] = [];
constructor(
private http: HttpClient,
) { }
get<T>(url: string, cacheTime?: number, forceRefresh: boolean = false)
: Observable<T> {
let cachedItem: CacheItem<T> = this.getCachedItem<T>(url);
if (cachedItem != undefined && !forceRefresh) {
let expireDate = cachedItem.timestampCached + cacheTime;
if (Date.now() < expireDate) {
return of(cachedItem.data);
}
}
return this.http.get<T>(url).pipe(
map(data => {
if (cacheTime) { // if we actually want to cache the result
if (cachedItem == undefined) {
cachedItem = new CacheItem();
cachedItem.url = url;
this.cache.push(cachedItem);
}
cachedItem.data = data;
cachedItem.timestampCached = Date.now();
}
return data;
})
);
}
private getCachedItem<T>(url: string): CacheItem<T> {
return this.cache.find(item => item.url == url);
}
}
And then just use MyCachedHttpClient
instead of HttpClient
everywhere.
Notes:
HttpClient
's get()
function since I didn't reimplemented the options
parameter here.I'm not too sure what the difference between news.service.ts
and news-list.service.ts
is but the main concept is that you need to separate concerns. The most basic separation you can do is by distiguishing "data providers" from "data consumers"
This could be anything that fetches and manages data. Whether in-memory cached data, a service call, etc. In your example, it seems to me that news.service.ts
it's a Web API client/proxy for everything related to news.
If this is a small file you could move all news-related cache management to this file or...create another component that manages cache and wraps news.service.ts
. That component would serve data from its cache, if the cache doesn't exist or has expired, then it calls on news.service.ts
methods. This way news.service.ts
's only responsbility is to make ajax requests to the API.
Here's an example without promises, observables or error handling just to give you an idea...
class NewsProvider{
constructor(){
this.newsCache = [];
}
getNewsItemById(id){
const item = this.newsCache.filter((i) => {i.id === id});
if(item.length === 1) return item[0];
this.newsCache = newsService.getAllNews();
item = this.newsCache.filter((i) => {i.id === id});
if(item.length === 1) return item[0];
else return null;
}
}
These would be any component that needs data, a news ticker in the home page, a badge notification somewhere in the navigation menu....there could be any components (or views) needing news-related data. For that reason, these components/views shouldn't need to know anything about where that data is coming from.
These components will use "data providers" as there main source of data
Cache only needs to be (and can be) managed in one single place as well as network-related operations
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With