Skip to content
This repository was archived by the owner on Aug 23, 2022. It is now read-only.

Commit 7bf2efb

Browse files
committed
Forcing async validators to run when concurrent to sync validators for invalid fields. Fixes #817
1 parent f09c807 commit 7bf2efb

File tree

2 files changed

+74
-27
lines changed

2 files changed

+74
-27
lines changed

src/components/control-component.js

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,8 @@ function createControlClass(s = defaultStrategy) {
272272
model,
273273
modelValue,
274274
updateOn,
275+
validateOn,
276+
asyncValidateOn,
275277
dispatch,
276278
getValue,
277279
} = this.props;
@@ -287,15 +289,18 @@ function createControlClass(s = defaultStrategy) {
287289

288290
// If any sync validity is invalid,
289291
// do not run async validation
290-
const asyncValidatorKeys = Object.keys(asyncValidators);
291-
const syncValid = Object.keys(fieldValue.validity).every((key) => {
292-
// If validity is based on async validator, skip
293-
if (!!~asyncValidatorKeys.indexOf(key)) return true;
294-
295-
return fieldValue.validity[key];
296-
});
292+
// unless sync and async validation occur simultaneously
293+
if (validateOn !== asyncValidateOn) {
294+
const asyncValidatorKeys = Object.keys(asyncValidators);
295+
const syncValid = Object.keys(fieldValue.validity).every((key) => {
296+
// If validity is based on async validator, skip
297+
if (!!~asyncValidatorKeys.indexOf(key)) return true;
298+
299+
return fieldValue.validity[key];
300+
});
297301

298-
if (!syncValid) return false;
302+
if (!syncValid) return false;
303+
}
299304

300305
dispatch(actions.setValidating(model, true));
301306

@@ -306,7 +311,7 @@ function createControlClass(s = defaultStrategy) {
306311
dispatch(actions.setValidity(model, validity));
307312
};
308313

309-
validator(getValue(valueToValidate), outerDone);
314+
validator(getValue(valueToValidate, this.props), outerDone);
310315
});
311316

312317
return valueToValidate;
@@ -481,12 +486,15 @@ function createControlClass(s = defaultStrategy) {
481486
model,
482487
updateOn,
483488
validateOn = updateOn,
489+
asyncValidateOn,
484490
} = this.props;
485491

486492
const eventActions = [
487493
eventAction && eventAction(model),
488494
(forceUpdate || containsEvent(validateOn, eventName))
489495
&& this.getValidateAction(persistedEvent, eventName, forceUpdate),
496+
(forceUpdate || containsEvent(asyncValidateOn, eventName))
497+
&& this.getAsyncValidateAction(persistedEvent, eventName),
490498
(forceUpdate || containsEvent(updateOn, eventName))
491499
&& this.getChangeAction(persistedEvent),
492500
];
@@ -498,7 +506,6 @@ function createControlClass(s = defaultStrategy) {
498506

499507
return (event, forceUpdate = false) => {
500508
const {
501-
asyncValidateOn,
502509
controlProps,
503510
parser,
504511
ignore,
@@ -526,13 +533,6 @@ function createControlClass(s = defaultStrategy) {
526533
}
527534

528535
return compose(
529-
(e) => {
530-
if (containsEvent(asyncValidateOn, eventName)) {
531-
this.getAsyncValidateAction(e, eventName);
532-
}
533-
534-
return e;
535-
},
536536
(e) => dispatchBatchActions(e, forceUpdate),
537537
parser,
538538
(e) => this.getValue(e),

test/control-component-spec.js

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -840,19 +840,32 @@ Object.keys(testContexts).forEach((testKey) => {
840840

841841
const field = TestUtils.renderIntoDocument(
842842
<Provider store={store}>
843-
<Control.text
844-
model="test.foo"
845-
validators={{
846-
required: (val) => val && val.length,
847-
}}
848-
asyncValidators={{
849-
asyncValid: (_, asyncDone) => asyncDone(false),
850-
}}
851-
/>
843+
<div>
844+
<Control.text
845+
model="test.foo"
846+
validators={{
847+
required: (val) => val && val.length,
848+
}}
849+
asyncValidators={{
850+
asyncValid: (_, asyncDone) => asyncDone(false),
851+
}}
852+
/>
853+
<Control.text
854+
model="test.bar"
855+
validators={{
856+
required: (val) => val && val.length,
857+
}}
858+
asyncValidators={{
859+
asyncValid: (_, asyncDone) => asyncDone(false),
860+
}}
861+
validateOn="change"
862+
asyncValidateOn="change"
863+
/>
864+
</div>
852865
</Provider>
853866
);
854867

855-
const input = TestUtils.findRenderedDOMComponentWithTag(field, 'input');
868+
const [input, concurrentInput] = TestUtils.scryRenderedDOMComponentsWithTag(field, 'input');
856869

857870
it('async validation should not run when field is invalid', () => {
858871
input.value = '';
@@ -868,6 +881,40 @@ Object.keys(testContexts).forEach((testKey) => {
868881
assert.isUndefined(store.getState().testForm.foo.validity.asyncValid);
869882
});
870883

884+
it('async validation should run on invalid field '
885+
+ 'when concurrent with sync validation', () => {
886+
concurrentInput.value = '';
887+
TestUtils.Simulate.change(concurrentInput);
888+
889+
assert.deepEqual(
890+
store.getState().testForm.bar.validity,
891+
{
892+
required: false,
893+
asyncValid: false,
894+
});
895+
assert.isDefined(store.getState().testForm.bar.validity.asyncValid);
896+
897+
concurrentInput.value = 'changed';
898+
TestUtils.Simulate.change(concurrentInput);
899+
900+
assert.deepEqual(
901+
store.getState().testForm.bar.validity,
902+
{
903+
required: true,
904+
asyncValid: false,
905+
});
906+
907+
concurrentInput.value = '';
908+
TestUtils.Simulate.change(concurrentInput);
909+
910+
assert.deepEqual(
911+
store.getState().testForm.bar.validity,
912+
{
913+
required: false,
914+
asyncValid: false,
915+
});
916+
});
917+
871918
it('async validation should not override sync validity', () => {
872919
input.value = 'asdf';
873920
TestUtils.Simulate.change(input);

0 commit comments

Comments
 (0)