rxjs observable angular 2 on localstorage change
I wrote a StorageService to support Observable localStorage and sessionStorage. It supports both in one service.
StorageService
import { BehaviorSubject, Observable } from 'rxjs';
/**
* Storage service
* used for persist application data in observable key value pair
*/
export class StorageService {
private storage: Storage;
private subjects: Map<string, BehaviorSubject<any>>;
/**
* Constructor with service injection
* @param storage
*/
constructor(storage: Storage) {
this.storage = storage;
this.subjects = new Map<string, BehaviorSubject<any>>();
}
/**
* watch data of given key
* @param key
* @param defaultValue
*/
watch(key: string): Observable<any> {
if (!this.subjects.has(key)) {
this.subjects.set(key, new BehaviorSubject<any>(null));
}
var item = this.storage.getItem(key);
if (item === "undefined") {
item = undefined;
} else {
item = JSON.parse(item);
}
this.subjects.get(key).next(item);
return this.subjects.get(key).asObservable();
}
/**
* get data of given key
* @param key
*/
get(key: string): any {
var item = this.storage.getItem(key);
if (item === "undefined") {
item = undefined;
} else {
item = JSON.parse(item);
}
return item;
}
/**
* set value on given key
* @param key
* @param value
*/
set(key: string, value: any) {
this.storage.setItem(key, JSON.stringify(value));
if (!this.subjects.has(key)) {
this.subjects.set(key, new BehaviorSubject<any>(value));
} else {
this.subjects.get(key).next(value);
}
}
/**
* remove given key
* @param key
*/
remove(key: string) {
if (this.subjects.has(key)) {
this.subjects.get(key).complete();
this.subjects.delete(key);
}
this.storage.removeItem(key);
}
/**
* clear all available keys
*/
clear() {
this.subjects.clear();
this.storage.clear();
}
}
LocalStorageService
import { Injectable, Inject } from '@angular/core';
import { StorageService } from './storage.service';
/**
* Local storage service
* used for persist application data in observable key value pair
*/
@Injectable()
export class LocalStorageService extends StorageService {
/**
* Constructor with service injection
* @param window
*/
constructor(@Inject('WINDOW') private window: any) {
super(window.localStorage);
}
}
SessionStorageService
import { Injectable, Inject } from '@angular/core';
import { StorageService } from './storage.service';
/**
* Session storage service
* used for persist application data in observable key value pair
*/
@Injectable()
export class SessionStorageService extends StorageService {
/**
* Constructor with service injection
* @param window
*/
constructor(@Inject('WINDOW') private window: any) {
super(window.sessionStorage);
}
}
This is how you can use the service:
import { LocalStorageService } from './local-storage.service';
export class TestClass implements OnInit, OnDestroy {
constructor(
private localStorage: LocalStorageService,
) { }
ngOnInit() {
// get current value
this.localStorage.get('foo');
// set new value
this.localStorage.set('foo', 'bar');
// watch value changes
this.localStorage.watch('foo').pipe(takeUntil(this.unsubscribe)).subscribe(foo => console.log('foo changed', foo));
}
ngOnDestroy() {
this.unsubscribe.next();
this.unsubscribe.complete();
}
}
(I'm really new to TypeScript with a couple of month experience. Any improvements or recommendations are welcome :-) )
What you want is a Subject. Check out the docs here.
For a quick example, something like this:
export class UserService {
...
private logger = new Subject<boolean>();
...
isLoggedIn(): Observable<boolean> {
return this.logger.asObservable();
}
logIn(provider: string, providerResponse: string) {
localStorage.setItem('authParams', providerResponse);
this.loggedIn = true;
this.logger.next(this.loggedIn);
}
logOut() {
localStorage.removeItem('authParams');
this.loggedIn = false;
this.logger.next(this.loggedIn);
}
...
An alternative is to observe the storage
event
fromEvent(window, 'storage').subscribe((storageEvent) => {
//do what you need to do with your storageEvent
})
This means you dont need to wrap the native API in any service layer.