Skip to content

Instantly share code, notes, and snippets.

@PatrickJS
Last active September 18, 2017 18:07
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save PatrickJS/1852053c7e629c9ea3720415c381ecd3 to your computer and use it in GitHub Desktop.
Save PatrickJS/1852053c7e629c9ea3720415c381ecd3 to your computer and use it in GitHub Desktop.
Universal Cache
// Something like this. I haven't tested this file
@Injectable()
export class ApiService {
constructor(public _http: Http, public _cache: Cache) {
}
// whatever domain/feature method name
getModel(url) {
// you want to return the cache if there is a response in it. This would cache the first response so if your API isn't idempotent you probably want to remove the item from the cache after you use it. LRU of 1
let key = url;
if (this._cache.has(key)) {
return Observable.of(this._cache.get(key));
}
// you probably shouldn't .share() and you should write the correct logic
return this._http.get(url)
.map(res => res.json())
.do(json => {
this._cache.set(key, json);
})
.share();
}
}
// Something like this. I haven't tested this file
@Component({
selector: 'app',
template: `
<ul>
<li *ngFor="let item of collection">
{{ item | json }}
</li>
</ul>
`
})
export class AppComponent {
collection: any;
constructor(public api: ApiService) {
this.api.getModel('/data.json').subscribe((json) => this.collection = json)
}
}
// Something like this. I haven't tested this file
@NgModule({
declarations: [
AppComponent
],
bootstrap: [
AppComponent
],
imports: [ UniversalModule ],
providers: [ ApiService, Cache ]
})
export class AppModule {
constructor(public cache: Cache) {
}
// we need to use the arrow function here to bind the context as this is a gotcha in Universal for now until it's fixed
universalDoDehydrate = (universalCache) => {
let self = this;
universalCache['Cache'] = JSON.stringify(self.cache.dehydrate());
}
universalAfterDehydrate = () => {
let self = this;
self.cache.clear();
}
}
// Something like this. I haven't tested this file
import { isBrowser } from 'angular2-universal'; // might have problems with AoT so use DI to determine if browser for now
import { Injectable } from '@angular/core';
function rehydrateCache(defaultValue) {
if (!isBrowser) { return defaultValue; }
const win: any = window;
if (win['UNIVERSAL_CACHE'] && win['UNIVERSAL_CACHE']['Cache']) {
let serverCache = defaultValue;
try {
serverCache = JSON.parse(win['UNIVERSAL_CACHE']['Cache']);
if (typeof serverCache !== typeof defaultValue) {
serverCache = defaultValue;
}
} catch (e) {
serverCache = defaultValue;
}
return serverCache;
}
return defaultValue;
}
@Injectable()
export class Cache {
_cache = {}; // use LRU on the server
constructor() {
if (isBrowser) {
let serverCache = rehydrateCache(this._cache);
this.rehydrate(serverCache);
}
}
has(key: string): boolean {
return this._cache.hasOwnProperty(key);
}
set(key: string, value: any): void {
this._cache[key] = value;
}
get(key: string): any {
return this._cache[key];
}
clear(): void {
Object.keys(this._cache).forEach((key) => {
delete this._cache[key];
});
}
dehydrate() {
let json = {};
Object.keys(this._cache).forEach((key: string) => {
json[key] = this._cache[key];
});
return json;
}
rehydrate(json) {
Object.keys(json).forEach((key: string) => {
this._cache[key] = json[key];
});
}
toJSON() {
return this.dehydrate();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment