diff --git a/projects/ngneat/forms-manager/src/lib/forms-manager.ts b/projects/ngneat/forms-manager/src/lib/forms-manager.ts index 342f86f..4f9b305 100644 --- a/projects/ngneat/forms-manager/src/lib/forms-manager.ts +++ b/projects/ngneat/forms-manager/src/lib/forms-manager.ts @@ -1,13 +1,14 @@ import { Inject, Injectable, Optional } from '@angular/core'; -import { AbstractControl, FormArray, FormGroup } from '@angular/forms'; +import { AbstractControl, FormGroup, FormArray } from '@angular/forms'; +import { coerceArray, filterControlKeys, filterNil, isBrowser, mergeDeep, wrapIntoObservable } from './utils'; import { EMPTY, merge, Observable, Subject, Subscription, timer } from 'rxjs'; -import { debounce, distinctUntilChanged, filter, map, mapTo } from 'rxjs/operators'; -import { deleteControl, findControl, handleFormArray, toStore } from './builders'; -import { Config, NgFormsManagerConfig, NG_FORMS_MANAGER_CONFIG } from './config'; +import { debounce, distinctUntilChanged, filter, first, map, mapTo, take } from 'rxjs/operators'; import { FormsStore } from './forms-manager.store'; -import { isEqual } from './isEqual'; import { Control, ControlFactory, FormKeys, HashMap, UpsertConfig } from './types'; -import { coerceArray, filterControlKeys, filterNil, isBrowser, mergeDeep } from './utils'; +import { Config, NG_FORMS_MANAGER_CONFIG, NgFormsManagerConfig } from './config'; +import { isEqual } from './isEqual'; +import { deleteControl, findControl, handleFormArray, toStore } from './builders'; +import { LocalStorageManager } from "./localStorageManager"; const NO_DEBOUNCE = Symbol('NO_DEBOUNCE'); @@ -17,6 +18,7 @@ export class NgFormsManager { private valueChanges$$: Map = new Map(); private instances$$: Map = new Map(); private initialValues$$: Map = new Map(); + private persistManager = new LocalStorageManager(); private destroy$$ = new Subject(); constructor(@Optional() @Inject(NG_FORMS_MANAGER_CONFIG) private config: NgFormsManagerConfig) { @@ -266,28 +268,6 @@ export class NgFormsManager { } } - /** - * - * @example - * - * A proxy to the original `reset` method - * - * manager.reset('login', { email: '' }); - * - */ - reset( - name: T, - value?: Partial, - options?: { - onlySelf?: boolean; - emitEvent?: boolean; - } - ) { - if (this.instances$$.has(name)) { - this.instances$$.get(name).reset(value, options); - } - } - /** * * Sets the initial value for a control @@ -490,7 +470,7 @@ export class NgFormsManager { * * @example * - * Removes the control from the store and from LocalStorage + * Removes the control from the store and from given PersistStorageManager * * manager.clear('login'); * @@ -535,13 +515,16 @@ export class NgFormsManager { this.setInitialValue(name, control.value); } - if (isBrowser() && config.persistState && this.hasControl(name) === false) { - const storageValue = this.getFromStorage(mergedConfig.storage.key); - if (storageValue[name]) { - this.store.update({ - [name]: mergeDeep(toStore(name, control), storageValue[name]), - } as Partial); - } + if ((isBrowser() || !(config.persistManager instanceof LocalStorageManager)) && config.persistState && this.hasControl(name) === false) { + this.persistManager = config.persistManager || this.persistManager; + this.getFromStorage(mergedConfig.storage.key).subscribe(value => { + const storageValue = value; + if (storageValue[name]) { + this.store.update({ + [name]: mergeDeep(toStore(name, control), storageValue[name]), + } as Partial); + } + }); } /** If the control already exist, patch the control with the store value */ @@ -593,19 +576,26 @@ export class NgFormsManager { } private removeFromStorage() { - localStorage.setItem(this.config.merge().storage.key, JSON.stringify(this.store.getValue())); + wrapIntoObservable(this.persistManager.setValue( + this.config.merge().storage.key, + this.store.getValue() + )).pipe(first()).subscribe() } private updateStorage(name: keyof FormsState, value: any, config) { if (isBrowser() && config.persistState) { - const storageValue = this.getFromStorage(config.storage.key); - storageValue[name] = filterControlKeys(value); - localStorage.setItem(config.storage.key, JSON.stringify(storageValue)); + this.getFromStorage(config.storage.key).pipe(first()).subscribe(valueFromStorage => { + const storageValue = valueFromStorage; + storageValue[name] = filterControlKeys(value); + wrapIntoObservable(this.persistManager.setValue(config.storage.key, storageValue)).pipe(first()).subscribe(); + }); } } private getFromStorage(key: string) { - return JSON.parse(localStorage.getItem(key) || '{}'); + return wrapIntoObservable(this.persistManager.getValue(key)).pipe( + take(1), + ); } private deleteControl(name: FormKeys) { diff --git a/projects/ngneat/forms-manager/src/lib/localStorageManager.ts b/projects/ngneat/forms-manager/src/lib/localStorageManager.ts new file mode 100644 index 0000000..fe42e4d --- /dev/null +++ b/projects/ngneat/forms-manager/src/lib/localStorageManager.ts @@ -0,0 +1,12 @@ +import { PersistManager } from "./types"; + +export class LocalStorageManager implements PersistManager { + setValue(key: string, data: T): T { + localStorage.setItem(key, JSON.stringify(data)); + return data; + } + + getValue(key: string): T { + return JSON.parse(localStorage.getItem(key) || '{}'); + } +} diff --git a/projects/ngneat/forms-manager/src/lib/sessionStorageManager.ts b/projects/ngneat/forms-manager/src/lib/sessionStorageManager.ts new file mode 100644 index 0000000..e26f853 --- /dev/null +++ b/projects/ngneat/forms-manager/src/lib/sessionStorageManager.ts @@ -0,0 +1,12 @@ +import { PersistManager } from "./types"; + +export class SessionStorageManager implements PersistManager { + setValue(key: string, data: T): T { + sessionStorage.setItem(key, JSON.stringify(data)); + return data; + } + + getValue(key: string): T { + return JSON.parse(sessionStorage.getItem(key) || '{}'); + } +} diff --git a/projects/ngneat/forms-manager/src/lib/types.ts b/projects/ngneat/forms-manager/src/lib/types.ts index 5a5252e..9dba10d 100644 --- a/projects/ngneat/forms-manager/src/lib/types.ts +++ b/projects/ngneat/forms-manager/src/lib/types.ts @@ -1,4 +1,5 @@ import { AbstractControl } from '@angular/forms'; +import { Observable } from "rxjs"; export type Control = Pick< AbstractControl, @@ -21,9 +22,17 @@ export interface HashMap { export type FormKeys = keyof FormsState | (keyof FormsState)[]; -export interface UpsertConfig { +export interface UpsertConfig { persistState?: boolean; debounceTime?: number; + persistManager?: PersistManager arrControlFactory?: ControlFactory | HashMap; withInitialValue?: boolean; } + +export interface PersistManager { + getValue(key: string): MaybeAsync; + setValue(key: string, value: T): MaybeAsync; +} + +export type MaybeAsync = Promise | Observable | T; diff --git a/projects/ngneat/forms-manager/src/lib/utils.ts b/projects/ngneat/forms-manager/src/lib/utils.ts index 640ffa2..571f87f 100644 --- a/projects/ngneat/forms-manager/src/lib/utils.ts +++ b/projects/ngneat/forms-manager/src/lib/utils.ts @@ -1,4 +1,4 @@ -import { Observable } from 'rxjs'; +import { from, isObservable, Observable, of } from 'rxjs'; import { filter } from 'rxjs/operators'; export type Diff = T extends U ? never : T; @@ -103,3 +103,15 @@ export function mergeDeep(target, ...sources) { return mergeDeep(target, ...sources); } + +export function isPromise(value: any): value is Promise { + return typeof value?.then === 'function'; +} + +export function wrapIntoObservable(value: T | Promise | Observable): Observable { + if (isObservable(value) || isPromise(value)) { + return from(value); + } + + return of(value); +} diff --git a/projects/ngneat/forms-manager/src/public_api.ts b/projects/ngneat/forms-manager/src/public_api.ts index 8e41315..4e23600 100644 --- a/projects/ngneat/forms-manager/src/public_api.ts +++ b/projects/ngneat/forms-manager/src/public_api.ts @@ -1,3 +1,6 @@ export { NgFormsManager } from './lib/forms-manager'; export { setAsyncValidators, setValidators } from './lib/validators'; export { NgFormsManagerConfig, NG_FORMS_MANAGER_CONFIG } from './lib/config'; +export { PersistManager } from './lib/types'; +export { LocalStorageManager } from './lib/localStorageManager'; +export { SessionStorageManager } from './lib/sessionStorageManager';