Skip to content

Commit 03c63c5

Browse files
authored
Merge pull request #18 from alexcotelin/fix-update-on-blur-debounce
fix: do not debounce for update on blur
2 parents 0a15799 + a5fcdbd commit 03c63c5

File tree

2 files changed

+73
-7
lines changed

2 files changed

+73
-7
lines changed

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

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1240,4 +1240,46 @@ describe('FormsManager', () => {
12401240
expect(formsManager.getInitialValue('other')).toBeUndefined();
12411241
});
12421242
});
1243+
1244+
describe('Debounce', () => {
1245+
it('should update value after default debounce of 300ms for update on change controls', fakeAsync(() => {
1246+
const updateOnChangeGroup = new FormGroup({
1247+
name: new FormControl(null, { updateOn: 'change' }),
1248+
});
1249+
formsManager.upsert('updateOnChangeGroup', updateOnChangeGroup);
1250+
1251+
updateOnChangeGroup.get('name').patchValue('Smith');
1252+
1253+
tick(100);
1254+
expect(formsManager.getControl('updateOnChangeGroup', 'name').value).toEqual(null);
1255+
1256+
tick(301);
1257+
expect(formsManager.getControl('updateOnChangeGroup', 'name').value).toEqual('Smith');
1258+
}));
1259+
1260+
it('should skip debounce and update value immediately for a form group set to update on blur', () => {
1261+
const updateOnBlurGroup = new FormGroup(
1262+
{
1263+
name: new FormControl(),
1264+
},
1265+
{ updateOn: 'blur' }
1266+
);
1267+
formsManager.upsert('updateOnBlurGroup', updateOnBlurGroup);
1268+
1269+
updateOnBlurGroup.get('name').patchValue('Smith');
1270+
1271+
expect(formsManager.getControl('updateOnBlurGroup', 'name').value).toEqual('Smith');
1272+
});
1273+
1274+
it('should skip debounce and update value immediately for a form control set to update on blur', () => {
1275+
const updateOnBlurGroup = new FormGroup({
1276+
name: new FormControl(null, { updateOn: 'blur' }),
1277+
});
1278+
formsManager.upsert('updateOnBlurGroup', updateOnBlurGroup);
1279+
1280+
updateOnBlurGroup.get('name').patchValue('Smith');
1281+
1282+
expect(formsManager.getControl('updateOnBlurGroup', 'name').value).toEqual('Smith');
1283+
});
1284+
});
12431285
});

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

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import { Inject, Injectable, Optional } from '@angular/core';
2-
import { AbstractControl } from '@angular/forms';
3-
import { coerceArray, filterControlKeys, filterNil, isBrowser, isObject, mergeDeep } from './utils';
4-
import { merge, Observable, Subject, Subscription } from 'rxjs';
5-
import { debounceTime, distinctUntilChanged, filter, map } from 'rxjs/operators';
2+
import { AbstractControl, FormGroup } from '@angular/forms';
3+
import { coerceArray, filterControlKeys, filterNil, isBrowser, mergeDeep } from './utils';
4+
import { EMPTY, merge, Observable, Subject, Subscription, timer } from 'rxjs';
5+
import { debounce, distinctUntilChanged, filter, map, mapTo, tap } from 'rxjs/operators';
66
import { FormsStore } from './forms-manager.store';
77
import { Control, ControlFactory, FormKeys, HashMap, UpsertConfig } from './types';
88
import { Config, NG_FORMS_MANAGER_CONFIG, NgFormsManagerConfig } from './config';
99
import { isEqual } from './isEqual';
1010
import { deleteControl, findControl, handleFormArray, toStore } from './builders';
1111

12+
const NO_DEBOUNCE = Symbol('NO_DEBOUNCE');
13+
1214
@Injectable({ providedIn: 'root' })
1315
export class NgFormsManager<FormsState = any> {
1416
private readonly store: FormsStore<FormsState>;
@@ -374,10 +376,10 @@ export class NgFormsManager<FormsState = any> {
374376
}
375377

376378
const unsubscribe = merge(
377-
control.valueChanges,
378-
control.statusChanges.pipe(distinctUntilChanged())
379+
control.statusChanges.pipe(distinctUntilChanged()),
380+
...this.getValueChangeStreams(control)
379381
)
380-
.pipe(debounceTime(mergedConfig.debounceTime))
382+
.pipe(debounce(value => (value === NO_DEBOUNCE ? EMPTY : timer(mergedConfig.debounceTime))))
381383
.subscribe(() => {
382384
const value = this.updateStore(name, control);
383385
this.updateStorage(name, value, mergedConfig);
@@ -389,6 +391,28 @@ export class NgFormsManager<FormsState = any> {
389391
return this;
390392
}
391393

394+
private getValueChangeStreams(control: AbstractControl) {
395+
const streams = [];
396+
397+
if (control.updateOn === 'blur') {
398+
streams.push(control.valueChanges.pipe(mapTo(NO_DEBOUNCE)));
399+
} else {
400+
streams.push(control.valueChanges);
401+
402+
if (control instanceof FormGroup) {
403+
return Object.keys(control.controls).reduce(
404+
(previous, key) =>
405+
control.get(key).updateOn === 'blur'
406+
? [...previous, control.get(key).valueChanges.pipe(mapTo(NO_DEBOUNCE))]
407+
: [...previous],
408+
streams
409+
);
410+
}
411+
}
412+
413+
return streams;
414+
}
415+
392416
private removeFromStorage() {
393417
localStorage.setItem(this.config.merge().storage.key, JSON.stringify(this.store.getValue()));
394418
}

0 commit comments

Comments
 (0)