Skip to content

Commit

Permalink
feat: persist values of disabled controls
Browse files Browse the repository at this point in the history
  • Loading branch information
va-stefanek committed Jan 25, 2021
1 parent 2db58bc commit 9dae31f
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 7 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,7 @@ Change the target storage or `debounceTime` value by providing options as a seco
| `debounceTime` | Update delay in ms between value changes | 250 |
| `manager` | A manager implementing the `PersistManager` interface | `LocalStorageManager` |
| `arrControlFactory` | Factory functions for `FormArray` | |
| `persistDisabledControls` | Defines whether values of disabled controls should be persisted | false |


By default the library provides `LocalStorageManager` and `SessionStorageManager`. It's possible to store the form value into a custom storage. Just implement the `PersistManager` interface, and use it when calling the `persist` function.
Expand Down
6 changes: 4 additions & 2 deletions projects/ngneat/reactive-forms/src/lib/control-actions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ValidationErrors, FormArray as NgFormArray } from '@angular/forms';
import { defer, merge, Observable, of, Subscription } from 'rxjs';
import { distinctUntilChanged, map, tap, debounceTime, switchMap } from 'rxjs/operators';
import { distinctUntilChanged, map, debounceTime, switchMap } from 'rxjs/operators';
import { FormArray } from './formArray';
import { FormControl } from './formControl';
import { FormGroup } from './formGroup';
Expand Down Expand Up @@ -147,7 +147,9 @@ export function selectControlValue$<T, R>(
export function persistValue$<T>(control: FormGroup<T>, key: string, options: PersistOptions<T>): Observable<T> {
return control.valueChanges.pipe(
debounceTime(options.debounceTime),
switchMap(value => wrapIntoObservable(options.manager.setValue(key, value)))
switchMap(value =>
wrapIntoObservable(options.manager.setValue(key, options.persistDisabledControls ? control.getRawValue() : value))
)
);
}

Expand Down
41 changes: 38 additions & 3 deletions projects/ngneat/reactive-forms/src/lib/formGroup.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { fakeAsync, tick, flush } from '@angular/core/testing';
import { of, Subject, Observable, timer, from } from 'rxjs';
import { fakeAsync, tick } from '@angular/core/testing';
import { of, Subject, Observable, timer } from 'rxjs';
import { FormControl } from './formControl';
import { FormGroup } from './formGroup';
import { FormArray } from './formArray';
Expand Down Expand Up @@ -325,9 +325,10 @@ describe('FormGroup', () => {
const person: Person = { name: 'ewan', phone: { num: 5550153, prefix: 288 }, skills: ['acting', 'motorcycle'] };

it.each([[0], [300], [500]])(
'should persist',
'should persist without disabled controls',
fakeAsync((tickMs: number) => {
const control = createGroup();
control.getControl('skills').disable();
const debounceTime = 50;
const persistManager = {
getValue: jest.fn(),
Expand All @@ -343,10 +344,44 @@ describe('FormGroup', () => {
tick(debounceTime);
expect(persistManager.setValue).toHaveBeenCalledTimes(2);
expect(persistManager.setValue).toHaveBeenLastCalledWith('key', control.value);
expect(persistManager.setValue).not.toHaveBeenLastCalledWith('key', control.getRawValue());
if (tickMs) {
expect(persistValue).toBeFalsy();
tick(tickMs);
expect(persistValue.name).toEqual('ewan mc');
expect(persistValue.skills).toEqual(undefined);
}
})
);

it.each([[0], [300], [500]])(
'should persist with disabled controls',
fakeAsync((tickMs: number) => {
const control = createGroup();
control.getControl('skills').disable();
const debounceTime = 50;
const persistManager = {
getValue: jest.fn(),
setValue: jest.fn((key, value) => {
return tickMs ? timer(tickMs).pipe(switchMap(() => of(value))) : value;
})
};
let persistValue: Person;
control
.persist('key', { debounceTime, manager: persistManager, persistDisabledControls: true })
.subscribe(value => (persistValue = value));
control.getControl('name').setValue('ewan');
tick(debounceTime);
control.getControl('name').setValue('ewan mc');
tick(debounceTime);
expect(persistManager.setValue).toHaveBeenCalledTimes(2);
expect(persistManager.setValue).not.toHaveBeenLastCalledWith('key', control.value);
expect(persistManager.setValue).toHaveBeenLastCalledWith('key', control.getRawValue());
if (tickMs) {
expect(persistValue).toBeFalsy();
tick(tickMs);
expect(persistValue.name).toEqual('ewan mc');
expect(persistValue.skills).toEqual([]);
}
})
);
Expand Down
8 changes: 6 additions & 2 deletions projects/ngneat/reactive-forms/src/lib/formGroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -330,13 +330,17 @@ export class FormGroup<T extends Obj = any, E extends object = any> extends NgFo
disableControl(this, disable, opts);
}

persist(key: string, { debounceTime, manager, arrControlFactory }: PersistOptions<T>): Observable<T> {
persist(
key: string,
{ debounceTime, manager, arrControlFactory, persistDisabledControls }: PersistOptions<T>
): Observable<T> {
const persistManager = manager || new LocalStorageManager();
return this.restore(key, persistManager, arrControlFactory).pipe(
switchMap(() =>
persistValue$(this, key, {
debounceTime: debounceTime || 250,
manager: persistManager
manager: persistManager,
persistDisabledControls
})
)
);
Expand Down
1 change: 1 addition & 0 deletions projects/ngneat/reactive-forms/src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,4 +158,5 @@ export interface PersistOptions<T> {
debounceTime?: number;
manager?: PersistManager<T>;
arrControlFactory?: ControlFactoryMap<T>;
persistDisabledControls?: boolean;
}

0 comments on commit 9dae31f

Please sign in to comment.