-
-
Notifications
You must be signed in to change notification settings - Fork 56
/
persist.ts
131 lines (107 loc) Β· 3.94 KB
/
persist.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import { AbstractControl, UntypedFormArray } from "@angular/forms";
import { from, isObservable, Observable, of } from "rxjs";
import { debounceTime, switchMap, take, tap } from "rxjs/operators";
export interface PersistOptions<T> {
debounceTime?: number;
manager?: PersistManager<T>;
arrControlFactory?: ControlFactoryMap<T>;
persistDisabledControls?: boolean;
}
export function persistControl<T>(
control: AbstractControl,
key: string,
{ debounceTime, manager, arrControlFactory, persistDisabledControls }: PersistOptions<T>
): Observable<unknown> {
const persistManager = manager || new LocalStorageManager();
return restoreControl(control, key, persistManager, arrControlFactory).pipe(
switchMap(() =>
persistValue$(control, key, {
debounceTime: debounceTime || 250,
manager: persistManager,
persistDisabledControls
})
)
);
}
function persistValue$<T>(control: AbstractControl, key: string, options: PersistOptions<T>): Observable<T> {
return control.valueChanges.pipe(
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
debounceTime(options.debounceTime!),
switchMap(value =>
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
wrapIntoObservable(options.manager!.setValue(key, options.persistDisabledControls ? (control as any).getRawValue() : value))
)
);
}
export function restoreControl<T>(control: AbstractControl, key: string, manager: PersistManager<T>, arrControlFactory: ControlFactoryMap<T> | undefined): Observable<T> {
return wrapIntoObservable(manager.getValue(key)).pipe(
take(1),
tap(value => {
if (!value) return;
if (arrControlFactory) {
handleFormArrays(control, value, arrControlFactory);
}
control.patchValue(value, { emitEvent: false });
})
);
}
function handleFormArrays<T>(
control: AbstractControl,
formValue: T,
arrControlFactory: ControlFactoryMap<T>
) {
Object.keys(formValue).forEach(controlName => {
const value = (formValue as any)[controlName];
if (Array.isArray(value) && control.get(controlName) instanceof UntypedFormArray) {
if (!arrControlFactory || (arrControlFactory && !(controlName in arrControlFactory))) {
throw new Error(`Please provide arrControlFactory for ${controlName}`);
}
const current = control.get(controlName) as UntypedFormArray;
const fc = (arrControlFactory as any)[controlName]
clearFormArray(current);
value.forEach((v, i) => current.insert(i, fc(v)));
}
});
}
export function clearFormArray(control: UntypedFormArray) {
while (control.length !== 0) {
control.removeAt(0);
}
}
export function wrapIntoObservable<T>(value: T | Promise<T> | Observable<T>): Observable<T> {
if (isObservable(value) || isPromise(value)) {
return from(value);
}
return of(value);
}
function isPromise(value: any): value is Promise<unknown> {
return typeof value?.then === 'function';
}
export type ArrayKeys<T> = { [K in keyof T]: T[K] extends any[] ? K : never }[keyof T];
export type ControlFactory<T> = (value: T) => AbstractControl;
export type ControlFactoryMap<T> = {
[K in ArrayKeys<T>]?: ControlFactory<ArrayType<T[K]>>;
};
type ArrayType<T> = T extends Array<infer R> ? R : any;
export interface PersistManager<T> {
setValue(key: string, data: T): T | Promise<T> | Observable<T>;
getValue(key: string): T | Promise<T> | Observable<T>;
}
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) || '{}');
}
}
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) || '{}');
}
}