Caching Data From HttpClient in Angular 4

Joseph picture Joseph · Sep 26, 2017 · Viewed 10.6k times · Source

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']);
        });
  }

Answer

Martin Schneider picture Martin Schneider · Apr 1, 2018

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:

  • This is for Angular 6 / RxJS 6. See code from Edit History if you are below.
  • This is just a basic implementation that hides out many features of HttpClient's get() function since I didn't reimplemented the options parameter here.