Skip to content

Commit

Permalink
feat(lib): allow to add custom storage
Browse files Browse the repository at this point in the history
  • Loading branch information
va-stefanek committed May 26, 2021
1 parent 0ee5eff commit 5e4efc4
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 43 deletions.
72 changes: 31 additions & 41 deletions projects/ngneat/forms-manager/src/lib/forms-manager.ts
Original file line number Diff line number Diff line change
@@ -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');

Expand All @@ -17,6 +18,7 @@ export class NgFormsManager<FormsState = any> {
private valueChanges$$: Map<keyof FormsState, Subscription> = new Map();
private instances$$: Map<keyof FormsState, AbstractControl> = new Map();
private initialValues$$: Map<keyof FormsState, any> = new Map();
private persistManager = new LocalStorageManager();
private destroy$$ = new Subject();

constructor(@Optional() @Inject(NG_FORMS_MANAGER_CONFIG) private config: NgFormsManagerConfig) {
Expand Down Expand Up @@ -266,28 +268,6 @@ export class NgFormsManager<FormsState = any> {
}
}

/**
*
* @example
*
* A proxy to the original `reset` method
*
* manager.reset('login', { email: '' });
*
*/
reset<T extends keyof FormsState>(
name: T,
value?: Partial<FormsState[T]>,
options?: {
onlySelf?: boolean;
emitEvent?: boolean;
}
) {
if (this.instances$$.has(name)) {
this.instances$$.get(name).reset(value, options);
}
}

/**
*
* Sets the initial value for a control
Expand Down Expand Up @@ -490,7 +470,7 @@ export class NgFormsManager<FormsState = any> {
*
* @example
*
* Removes the control from the store and from LocalStorage
* Removes the control from the store and from given PersistStorageManager
*
* manager.clear('login');
*
Expand Down Expand Up @@ -535,13 +515,16 @@ export class NgFormsManager<FormsState = any> {
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<FormsState>);
}
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<FormsState>);
}
});
}

/** If the control already exist, patch the control with the store value */
Expand Down Expand Up @@ -593,19 +576,26 @@ export class NgFormsManager<FormsState = any> {
}

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<FormsState>) {
Expand Down
12 changes: 12 additions & 0 deletions projects/ngneat/forms-manager/src/lib/localStorageManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { PersistManager } from "./types";

export class LocalStorageManager<T> implements PersistManager<T> {
setValue(key: string, data: T): T {
localStorage.setItem(key, JSON.stringify(data));
return data;
}

getValue(key: string): T {
return JSON.parse(localStorage.getItem(key) || '{}');
}
}
12 changes: 12 additions & 0 deletions projects/ngneat/forms-manager/src/lib/sessionStorageManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { PersistManager } from "./types";

export class SessionStorageManager<T> implements PersistManager<T> {
setValue(key: string, data: T): T {
sessionStorage.setItem(key, JSON.stringify(data));
return data;
}

getValue(key: string): T {
return JSON.parse(sessionStorage.getItem(key) || '{}');
}
}
11 changes: 10 additions & 1 deletion projects/ngneat/forms-manager/src/lib/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { AbstractControl } from '@angular/forms';
import { Observable } from "rxjs";

export type Control<T = any> = Pick<
AbstractControl,
Expand All @@ -21,9 +22,17 @@ export interface HashMap<T = any> {

export type FormKeys<FormsState> = keyof FormsState | (keyof FormsState)[];

export interface UpsertConfig {
export interface UpsertConfig<T = any> {
persistState?: boolean;
debounceTime?: number;
persistManager?: PersistManager<T>
arrControlFactory?: ControlFactory | HashMap<ControlFactory>;
withInitialValue?: boolean;
}

export interface PersistManager<T> {
getValue(key: string): MaybeAsync<T>;
setValue(key: string, value: T): MaybeAsync<T>;
}

export type MaybeAsync<T = any> = Promise<T> | Observable<T> | T;
14 changes: 13 additions & 1 deletion projects/ngneat/forms-manager/src/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Observable } from 'rxjs';
import { from, isObservable, Observable, of } from 'rxjs';
import { filter } from 'rxjs/operators';

export type Diff<T, U> = T extends U ? never : T;
Expand Down Expand Up @@ -103,3 +103,15 @@ export function mergeDeep(target, ...sources) {

return mergeDeep(target, ...sources);
}

export function isPromise(value: any): value is Promise<unknown> {
return typeof value?.then === 'function';
}

export function wrapIntoObservable<T>(value: T | Promise<T> | Observable<T>): Observable<T> {
if (isObservable(value) || isPromise(value)) {
return from(value);
}

return of(value);
}
3 changes: 3 additions & 0 deletions projects/ngneat/forms-manager/src/public_api.ts
Original file line number Diff line number Diff line change
@@ -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';

0 comments on commit 5e4efc4

Please sign in to comment.