Skip to content

Commit 5e4efc4

Browse files
committed
feat(lib): allow to add custom storage
1 parent 0ee5eff commit 5e4efc4

File tree

6 files changed

+81
-43
lines changed

6 files changed

+81
-43
lines changed

projects/ngneat/forms-manager/src/lib/forms-manager.ts

+31-41
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { Inject, Injectable, Optional } from '@angular/core';
2-
import { AbstractControl, FormArray, FormGroup } from '@angular/forms';
2+
import { AbstractControl, FormGroup, FormArray } from '@angular/forms';
3+
import { coerceArray, filterControlKeys, filterNil, isBrowser, mergeDeep, wrapIntoObservable } from './utils';
34
import { EMPTY, merge, Observable, Subject, Subscription, timer } from 'rxjs';
4-
import { debounce, distinctUntilChanged, filter, map, mapTo } from 'rxjs/operators';
5-
import { deleteControl, findControl, handleFormArray, toStore } from './builders';
6-
import { Config, NgFormsManagerConfig, NG_FORMS_MANAGER_CONFIG } from './config';
5+
import { debounce, distinctUntilChanged, filter, first, map, mapTo, take } from 'rxjs/operators';
76
import { FormsStore } from './forms-manager.store';
8-
import { isEqual } from './isEqual';
97
import { Control, ControlFactory, FormKeys, HashMap, UpsertConfig } from './types';
10-
import { coerceArray, filterControlKeys, filterNil, isBrowser, mergeDeep } from './utils';
8+
import { Config, NG_FORMS_MANAGER_CONFIG, NgFormsManagerConfig } from './config';
9+
import { isEqual } from './isEqual';
10+
import { deleteControl, findControl, handleFormArray, toStore } from './builders';
11+
import { LocalStorageManager } from "./localStorageManager";
1112

1213
const NO_DEBOUNCE = Symbol('NO_DEBOUNCE');
1314

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

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

269-
/**
270-
*
271-
* @example
272-
*
273-
* A proxy to the original `reset` method
274-
*
275-
* manager.reset('login', { email: '' });
276-
*
277-
*/
278-
reset<T extends keyof FormsState>(
279-
name: T,
280-
value?: Partial<FormsState[T]>,
281-
options?: {
282-
onlySelf?: boolean;
283-
emitEvent?: boolean;
284-
}
285-
) {
286-
if (this.instances$$.has(name)) {
287-
this.instances$$.get(name).reset(value, options);
288-
}
289-
}
290-
291271
/**
292272
*
293273
* Sets the initial value for a control
@@ -490,7 +470,7 @@ export class NgFormsManager<FormsState = any> {
490470
*
491471
* @example
492472
*
493-
* Removes the control from the store and from LocalStorage
473+
* Removes the control from the store and from given PersistStorageManager
494474
*
495475
* manager.clear('login');
496476
*
@@ -535,13 +515,16 @@ export class NgFormsManager<FormsState = any> {
535515
this.setInitialValue(name, control.value);
536516
}
537517

538-
if (isBrowser() && config.persistState && this.hasControl(name) === false) {
539-
const storageValue = this.getFromStorage(mergedConfig.storage.key);
540-
if (storageValue[name]) {
541-
this.store.update({
542-
[name]: mergeDeep(toStore(name, control), storageValue[name]),
543-
} as Partial<FormsState>);
544-
}
518+
if ((isBrowser() || !(config.persistManager instanceof LocalStorageManager)) && config.persistState && this.hasControl(name) === false) {
519+
this.persistManager = config.persistManager || this.persistManager;
520+
this.getFromStorage(mergedConfig.storage.key).subscribe(value => {
521+
const storageValue = value;
522+
if (storageValue[name]) {
523+
this.store.update({
524+
[name]: mergeDeep(toStore(name, control), storageValue[name]),
525+
} as Partial<FormsState>);
526+
}
527+
});
545528
}
546529

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

595578
private removeFromStorage() {
596-
localStorage.setItem(this.config.merge().storage.key, JSON.stringify(this.store.getValue()));
579+
wrapIntoObservable(this.persistManager.setValue(
580+
this.config.merge().storage.key,
581+
this.store.getValue()
582+
)).pipe(first()).subscribe()
597583
}
598584

599585
private updateStorage(name: keyof FormsState, value: any, config) {
600586
if (isBrowser() && config.persistState) {
601-
const storageValue = this.getFromStorage(config.storage.key);
602-
storageValue[name] = filterControlKeys(value);
603-
localStorage.setItem(config.storage.key, JSON.stringify(storageValue));
587+
this.getFromStorage(config.storage.key).pipe(first()).subscribe(valueFromStorage => {
588+
const storageValue = valueFromStorage;
589+
storageValue[name] = filterControlKeys(value);
590+
wrapIntoObservable(this.persistManager.setValue(config.storage.key, storageValue)).pipe(first()).subscribe();
591+
});
604592
}
605593
}
606594

607595
private getFromStorage(key: string) {
608-
return JSON.parse(localStorage.getItem(key) || '{}');
596+
return wrapIntoObservable(this.persistManager.getValue(key)).pipe(
597+
take(1),
598+
);
609599
}
610600

611601
private deleteControl(name: FormKeys<FormsState>) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { PersistManager } from "./types";
2+
3+
export class LocalStorageManager<T> implements PersistManager<T> {
4+
setValue(key: string, data: T): T {
5+
localStorage.setItem(key, JSON.stringify(data));
6+
return data;
7+
}
8+
9+
getValue(key: string): T {
10+
return JSON.parse(localStorage.getItem(key) || '{}');
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { PersistManager } from "./types";
2+
3+
export class SessionStorageManager<T> implements PersistManager<T> {
4+
setValue(key: string, data: T): T {
5+
sessionStorage.setItem(key, JSON.stringify(data));
6+
return data;
7+
}
8+
9+
getValue(key: string): T {
10+
return JSON.parse(sessionStorage.getItem(key) || '{}');
11+
}
12+
}

projects/ngneat/forms-manager/src/lib/types.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { AbstractControl } from '@angular/forms';
2+
import { Observable } from "rxjs";
23

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

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

24-
export interface UpsertConfig {
25+
export interface UpsertConfig<T = any> {
2526
persistState?: boolean;
2627
debounceTime?: number;
28+
persistManager?: PersistManager<T>
2729
arrControlFactory?: ControlFactory | HashMap<ControlFactory>;
2830
withInitialValue?: boolean;
2931
}
32+
33+
export interface PersistManager<T> {
34+
getValue(key: string): MaybeAsync<T>;
35+
setValue(key: string, value: T): MaybeAsync<T>;
36+
}
37+
38+
export type MaybeAsync<T = any> = Promise<T> | Observable<T> | T;

projects/ngneat/forms-manager/src/lib/utils.ts

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Observable } from 'rxjs';
1+
import { from, isObservable, Observable, of } from 'rxjs';
22
import { filter } from 'rxjs/operators';
33

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

104104
return mergeDeep(target, ...sources);
105105
}
106+
107+
export function isPromise(value: any): value is Promise<unknown> {
108+
return typeof value?.then === 'function';
109+
}
110+
111+
export function wrapIntoObservable<T>(value: T | Promise<T> | Observable<T>): Observable<T> {
112+
if (isObservable(value) || isPromise(value)) {
113+
return from(value);
114+
}
115+
116+
return of(value);
117+
}
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
export { NgFormsManager } from './lib/forms-manager';
22
export { setAsyncValidators, setValidators } from './lib/validators';
33
export { NgFormsManagerConfig, NG_FORMS_MANAGER_CONFIG } from './lib/config';
4+
export { PersistManager } from './lib/types';
5+
export { LocalStorageManager } from './lib/localStorageManager';
6+
export { SessionStorageManager } from './lib/sessionStorageManager';

0 commit comments

Comments
 (0)