Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Form service amendment #244

Merged
merged 3 commits into from
Oct 11, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/editors/SuggestBox.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,11 @@ class SuggestBoxEditor extends React.Component {

async _onInputFocus(e) {
await this._openList();
findDOMNode(this.input).select();
const inputElement = findDOMNode(this.input);
// Sometimes the component may become unmounted while waiting this._openList(), so inputElement will be unavailable
if (inputElement) {
inputElement.select();
}
if (this.props.onFocus) {
this.props.onFocus(e);
}
Expand Down Expand Up @@ -569,7 +573,7 @@ class SuggestBoxEditor extends React.Component {
{...utils.omit(this.props,
['model', 'value', 'onChange', 'onLabelChange', 'onFocus',
'select', 'notFoundElement', 'loadingElement', 'defaultLabel', 'onMetadataChange'])}
ref={(input) => this.input = input }
ref={(input) => this.input = input}
type='text'
onClick={this._openList}
onFocus={this._onInputFocus}
Expand Down
62 changes: 33 additions & 29 deletions src/form/FormService.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class FormService {
this._warningsValidator = settings.warningsValidator || new Validator();

this.validating = false;
this._pendingClearValidation = [];
this._hiddenValidationFields = [];
this.submitting = false;
this._isNotInitialized = false;

Expand Down Expand Up @@ -158,23 +158,25 @@ class FormService {
this.model.off('update', this._onModelChange);
}

clearValidation(field) {
/**
* @param {String|String[]} fields
*/
clearValidation(fields) {
if (this._isNotInitialized) {
return;
}

if (this.validating) {
this._pendingClearValidation.push(field);
}

if (Array.isArray(field)) {
field.forEach(oneField => {
this._errors.clearField(oneField);
this._warnings.clearField(oneField);
});
// We keep info about _hiddenValidationFields for cases when clearValidation was called while validateForm was
// called and haven't finished, so then old validation result shouldn't show errors for _hiddenValidationFields
// fields, but the next called validations will clear _hiddenValidationFields so the fields will get errors again.
// Use case: a user changed field 'name', a validation started, the user focused field 'age' so we called
// clearValidation('age'), the validation finished and returned errors for fields 'name' and 'age', but we
// shouldn't show error for field 'age' because the user has just focused it. Then user blured field 'age', a new
// validation stated and it should show errors for field 'age'.
if (Array.isArray(fields)) {
this._hiddenValidationFields.push(...fields);
} else {
this._errors.clearField(field);
this._warnings.clearField(field);
this._hiddenValidationFields.push(fields);
}

this._setState();
Expand Down Expand Up @@ -325,6 +327,12 @@ class FormService {
return;
}

// We should remove only those hiddenValidationFields that were present before validation started and keep those
// that were added after validation started (so it is possible and ok that field 'name' may be present 2 times:
// 1 for old validation call and 1 for the new).
// Take into account that _validateForm is throttled, so next calls will be skipped or scheduled after current call
// finishes. It means we don't need to care about parallel calls because they are impossible.
const countOfHiddenValidationFieldsToRemove = this._hiddenValidationFields.length;
this.validating = true;

try {
Expand All @@ -335,11 +343,7 @@ class FormService {
} finally {
this.validating = false;

let field;
while (field = this._pendingClearValidation.pop()) {
this._warnings.clearField(field);
this._errors.clearField(field);
}
this._hiddenValidationFields.splice(0, countOfHiddenValidationFieldsToRemove);

this._setState();
}
Expand All @@ -358,8 +362,12 @@ class FormService {
newFields[fieldName] = {};
newFields[fieldName].value = data[fieldName];
newFields[fieldName].isChanged = changes.hasOwnProperty(fieldName);
newFields[fieldName].errors = errors ? errors.getFieldErrorMessages(fieldName) : null;
newFields[fieldName].warnings = warnings ? warnings.getFieldErrorMessages(fieldName) : null;
newFields[fieldName].errors = errors && !this._hiddenValidationFields.includes(fieldName) ?
errors.getFieldErrorMessages(fieldName) :
null;
newFields[fieldName].warnings = warnings && !this._hiddenValidationFields.includes(fieldName) ?
warnings.getFieldErrorMessages(fieldName) :
null;
return newFields;
}, {});
return this._wrapFields(fields);
Expand Down Expand Up @@ -397,17 +405,13 @@ class FormService {
* @returns {ValidationErrors} Form fields
*/
_applyPartialErrorChecking(validationErrors) {
const filteredErrors = validationErrors;

// If gradual validation is on, we need
// to remove unchanged records from changes object
if (!this._partialErrorChecking) {
return filteredErrors;
}
const filteredErrors = validationErrors.clone();

// Look through all form fields
for (const field in this._data) {
if (!this._changes.hasOwnProperty(field) || utils.isEqual(this._changes[field], this._data[field])) {
for (const field of Object.keys(this._data)) {
const isFieldPristine = !this._changes.hasOwnProperty(field) ||
utils.isEqual(this._changes[field], this._data[field]);
if (this._hiddenValidationFields.includes(field) || this._partialErrorChecking && isFieldPristine) {
filteredErrors.clearField(field);
}
}
Expand Down