Skip to content

Instantly share code, notes, and snippets.

@gilhanan
Forked from ckimrie/example.component.ts
Last active February 19, 2019 09:36
Show Gist options
  • Save gilhanan/77dab2062195d200b87c89d8aa17ae81 to your computer and use it in GitHub Desktop.
Save gilhanan/77dab2062195d200b87c89d8aa17ae81 to your computer and use it in GitHub Desktop.
Example on how to achieve RxJS observable caching and storage in Angular 2+. Ideal for storing Http requests client side for offline usage.
import { Component, OnInit, OnDestroy } from '@angular/core';
import {Http} from "@angular/http";
import { LocalCacheService } from "./local-cache.service";
@Component({
selector: 'app-example',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class ExampleComponent implements OnInit, OnDestroy {
constructor(public http:Http, public cache:LocalCacheService){
//Cache an observable
let requestObservable = this.http.get("http://example.com/path/to/api").map(res => res.json())
this.cache.observable('my-cache-key', requestObservable, 300).subscribe(result => {
//Use result
console.log(result)
});
}
}
import {Injectable} from "@angular/core";
import {LocalStorageService} from "./local-storage.service";
import {Observable} from "rxjs/Observable";
import {isEmpty, isString, isNumber, isDate} from 'lodash';
@Injectable()
export class LocalCacheService {
/**
* Default expiry in seconds
*
* @type {number}
*/
defaultExpires: number = 86400; //24Hrs
constructor(private localstorage: LocalStorageService) {}
/**
* Cache or use result from observable
*
* If cache key does not exist or is expired, observable supplied in argument is returned and result cached
*
* @param key
* @param observable
* @param expires
* @returns {Observable<T>}
*/
public observable<T>(key: string, observable: Observable<T>, expires:number = this.defaultExpires): Observable<T> {
//First fetch the item from localstorage (even though it may not exist)
return this.localstorage.getItem(key)
//If the cached value has expired, nullify it, otherwise pass it through
.map((val: CacheStorageRecord) => val && (new Date(val.expires)).getTime() > Date.now() ? val : null)
//At this point, if we encounter a null value, either it doesnt exist in the cache or it has expired.
//If it doesnt exist, simply return the observable that has been passed in, caching its value as it passes through
.flatMap((val: CacheStorageRecord | null) => {
if (!isEmpty(val)) {
return Observable.of(val.value);
} else {
return observable.flatMap((val:any) => this.value(key, val, expires)); //The result may have 'expires' explicitly set
}
})
}
/**
* Cache supplied value until expiry
*
* @param key
* @param value
* @param expires
* @returns {Observable<T>}
*/
value<T>(key:string, value:T, expires:number|string|Date = this.defaultExpires):Observable<T>{
let _expires:Date = this.sanitizeAndGenerateDateExpiry(expires);
return this.localstorage.setItem(key, {
expires: _expires,
value: value
}).map(val => val.value);
}
/**
*
* @param key
* @returns {Observable<null>}
*/
expire(key:string):Observable<null>{
return this.localstorage.removeItem(key);
}
/**
*
* @param expires
* @returns {Date}
*/
private sanitizeAndGenerateDateExpiry(expires:string|number|Date):Date{
let expiryDate:Date = this.expiryToDate(expires);
//Dont allow expiry dates in the past
if(expiryDate.getTime() <= Date.now()){
return new Date(Date.now() + this.defaultExpires);
}
return expiryDate;
}
/**
*
* @param expires
* @returns {Date}
*/
private expiryToDate(expires:number|string|Date):Date{
if(isNumber(expires)){
return new Date(Date.now() + Math.abs(expires)*1000);
}
if(isString(expires)){
return new Date(expires);
}
if(isDate(expires)){
return expires;
}
return new Date();
}
}
/**
* Cache storage record interface
*/
interface CacheStorageRecord {
expires: Date,
value: any
}
import * as localforage from 'localforage';
import {Injectable} from "@angular/core";
import {Observable} from "rxjs/Observable";
@Injectable()
export class LocalStorageService {
/**
*
* @param key
* @param value
* @returns {any}
*/
public setItem<T>(key:string, value:T):Observable<T>{
return Observable.fromPromise(localforage.setItem(key, value))
}
/**
*
* @param key
* @returns {any}
*/
public getItem<T>(key:string):Observable<T>{
return Observable.fromPromise(localforage.getItem(key))
}
/**
*
* @param key
* @returns {any}
*/
public removeItem(key:string):Observable<void>{
return Observable.fromPromise(localforage.removeItem(key))
}
}
@shan75
Copy link

shan75 commented Jun 23, 2018

This have build errors? is there any updated version?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment