Skip to content

Commit 07dfcfe

Browse files
committed
feat(lib): allow to add custom storage
1 parent 8946c8c commit 07dfcfe

File tree

6 files changed

+11503
-11980
lines changed

6 files changed

+11503
-11980
lines changed

README.md

Lines changed: 12 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -256,53 +256,25 @@ formsManager.upsert(formName, abstractContorl, {
256256
});
257257
```
258258

259-
By default, the state is persisted to `localStorage` ([Link](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage)).
260-
261-
For storage to `sessionStorage` ([Link](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage)), add `FORMS_MANAGER_SESSION_STORAGE_PROVIDER` to the providers array in `app.module.ts`:
259+
Library use `@ngneat/storage`([Link](https://github.com/ngneat/storage)) library which provides LocalStorageManager and SessionStorageManager. It's possible to store the form value into a custom storage. Just implement the PersistManager interface which is part of `@ngneat/storage` and use it when calling the upsert function. Storage implementation support Observables and Promises.
262260

263261
```ts
264-
import { FORMS_MANAGER_SESSION_STORAGE_PROVIDER } from '@ngneat/forms-manager';
262+
export class StateStoreManager<T> implements PersistManager<T> {
263+
setValue(key: string, data: T) {
264+
...
265+
}
265266

266-
@NgModule({
267-
declarations: [AppComponent],
268-
imports: [ ... ],
269-
providers: [
270-
...
271-
FORMS_MANAGER_SESSION_STORAGE_PROVIDER,
267+
getValue(key: string) {
272268
...
273-
],
274-
bootstrap: [AppComponent],
275-
})
276-
export class AppModule {}
269+
}
270+
}
277271
```
278272

279-
Furthermore, a **custom storage provider**, which must implement the `Storage` interface ([Link](https://developer.mozilla.org/en-US/docs/Web/API/Storage)) can be provided through the `FORMS_MANAGER_STORAGE` token:
280-
281273
```ts
282-
import { FORMS_MANAGER_STORAGE } from '@ngneat/forms-manager';
283-
284-
class MyStorage implements Storage {
285-
public clear() { ... }
286-
public key(index: number): string | null { ... }
287-
public getItem(key: string): string | null { ... }
288-
public removeItem(key: string) { ... }
289-
public setItem(key: string, value: string) { ... }
290-
}
291-
292-
@NgModule({
293-
declarations: [AppComponent],
294-
imports: [ ... ],
295-
providers: [
296-
...
297-
{
298-
provide: FORMS_MANAGER_STORAGE,
299-
useValue: MyStorage,
300-
},
301-
...
302-
],
303-
bootstrap: [AppComponent],
304-
})
305-
export class AppModule {}
274+
formsManager.upsert(formName, abstractContorl, {
275+
persistState: true,
276+
persistManager: new StateStoreManager(),
277+
});
306278
```
307279

308280
## Validators

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"@angular/platform-browser-dynamic": "~9.1.11",
2424
"@angular/router": "~9.1.11",
2525
"@ngneat/lib": "^1.0.1",
26+
"@ngneat/storage": "^1.0.0",
2627
"@schuchard/prettier": "^3.1.0",
2728
"core-js": "^2.5.4",
2829
"ngx-semantic-version": "^1.2.1",

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

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
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 } 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';
11-
import { FORMS_MANAGER_STORAGE } from './injection-tokens';
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 '@ngneat/storage';
12+
import { wrapIntoObservable } from '@ngneat/storage/lib/utils';
1213

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

@@ -19,12 +20,10 @@ export class NgFormsManager<FormsState = any> {
1920
private valueChanges$$: Map<keyof FormsState, Subscription> = new Map();
2021
private instances$$: Map<keyof FormsState, AbstractControl> = new Map();
2122
private initialValues$$: Map<keyof FormsState, any> = new Map();
23+
private persistManager = new LocalStorageManager();
2224
private destroy$$ = new Subject();
2325

24-
constructor(
25-
@Optional() @Inject(NG_FORMS_MANAGER_CONFIG) private config: NgFormsManagerConfig,
26-
@Inject(FORMS_MANAGER_STORAGE) private readonly browserStorage?: Storage
27-
) {
26+
constructor(@Optional() @Inject(NG_FORMS_MANAGER_CONFIG) private config: NgFormsManagerConfig) {
2827
this.store = new FormsStore({} as FormsState);
2928
}
3029

@@ -495,7 +494,7 @@ export class NgFormsManager<FormsState = any> {
495494
*
496495
* @example
497496
*
498-
* Removes the control from the store and from browser storage
497+
* Removes the control from the store and from given PersistStorageManager
499498
*
500499
* manager.clear('login');
501500
*
@@ -540,13 +539,20 @@ export class NgFormsManager<FormsState = any> {
540539
this.setInitialValue(name, control.value);
541540
}
542541

543-
if (isBrowser() && config.persistState && this.hasControl(name) === false) {
544-
const storageValue = this.getFromStorage(mergedConfig.storage.key);
545-
if (storageValue[name]) {
546-
this.store.update({
547-
[name]: mergeDeep(toStore(name, control), storageValue[name]),
548-
} as Partial<FormsState>);
549-
}
542+
if (
543+
(isBrowser() || !(config.persistManager instanceof LocalStorageManager)) &&
544+
config.persistState &&
545+
this.hasControl(name) === false
546+
) {
547+
this.persistManager = config.persistManager || this.persistManager;
548+
this.getFromStorage(mergedConfig.storage.key).subscribe(value => {
549+
const storageValue = value;
550+
if (storageValue[name]) {
551+
this.store.update({
552+
[name]: mergeDeep(toStore(name, control), storageValue[name]),
553+
} as Partial<FormsState>);
554+
}
555+
});
550556
}
551557

552558
/** If the control already exist, patch the control with the store value */
@@ -598,22 +604,29 @@ export class NgFormsManager<FormsState = any> {
598604
}
599605

600606
private removeFromStorage() {
601-
this.browserStorage?.setItem(
602-
this.config.merge().storage.key,
603-
JSON.stringify(this.store.getValue())
604-
);
607+
wrapIntoObservable(
608+
this.persistManager.setValue(this.config.merge().storage.key, this.store.getValue())
609+
)
610+
.pipe(first())
611+
.subscribe();
605612
}
606613

607614
private updateStorage(name: keyof FormsState, value: any, config) {
608615
if (isBrowser() && config.persistState) {
609-
const storageValue = this.getFromStorage(config.storage.key);
610-
storageValue[name] = filterControlKeys(value);
611-
this.browserStorage?.setItem(config.storage.key, JSON.stringify(storageValue));
616+
this.getFromStorage(config.storage.key)
617+
.pipe(first())
618+
.subscribe(valueFromStorage => {
619+
const storageValue = valueFromStorage;
620+
storageValue[name] = filterControlKeys(value);
621+
wrapIntoObservable(this.persistManager.setValue(config.storage.key, storageValue))
622+
.pipe(first())
623+
.subscribe();
624+
});
612625
}
613626
}
614627

615628
private getFromStorage(key: string) {
616-
return JSON.parse(this.browserStorage?.getItem(key) || '{}');
629+
return wrapIntoObservable(this.persistManager.getValue(key)).pipe(take(1));
617630
}
618631

619632
private deleteControl(name: FormKeys<FormsState>) {

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { AbstractControl } from '@angular/forms';
2+
import { PersistManager } from '@ngneat/storage';
23

34
export type Control<T = any> = Pick<
45
AbstractControl,
@@ -21,9 +22,10 @@ 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
}

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

Lines changed: 1 addition & 1 deletion
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;

0 commit comments

Comments
 (0)