src/core/validation/createValidator.js
import {
isObject,
omit,
pick,
isEqual,
isArray,
isBoolean,
isFunction,
each,
isEmpty,
find,
pickBy,
get,
compact,
map,
} from 'lodash';
import { isPromise } from '../../tools/helpers';
import * as presets from './presets';
import { addFieldMessage } from '../../actions/formPlugin';
import { batchActions } from 'redux-batched-actions/lib/index';
function findPriorityMessage(messages) {
return (
find(messages, { severity: 'danger' }) ||
find(messages, { severity: 'warning' }) ||
find(messages, { severity: 'success' })
);
}
/**
* есть ли ошибки или нет
* @param messages
* @returns {*}
*/
function hasError(messages) {
return [].concat
.apply([], Object.values(messages))
.reduce((res, msg) => msg.severity === 'danger' || res, false);
}
export default function createValidator(
validationConfig = {},
formName,
state
) {
return {
asyncValidate: validateField(validationConfig, formName, state),
asyncChangeFields: Object.keys(validationConfig || {}),
};
}
/**
* функция валидации
* @param validationConfig
* @param formName
* @param state
* @param isTouched
* @returns {Promise<any[]>}
*/
export const validateField = (
validationConfig,
formName,
state,
isTouched = false
) => (values, dispatch) => {
const registeredFields = get(state, ['form', formName, 'registeredFields']);
const fields = get(state, ['form', formName, 'fields']);
const validation = pickBy(validationConfig, (value, key) =>
get(registeredFields, `${key}.visible`, true)
);
const errors = {};
const promiseList = [Promise.resolve()];
const addError = (
fieldId,
{ text = true, severity = true },
options = {}
) => {
!errors[fieldId] && (errors[fieldId] = []);
errors[fieldId].push({});
const last = errors[fieldId].length - 1;
isBoolean(text)
? (errors[fieldId][last].text = options.text)
: (errors[fieldId][last].text = text);
isBoolean(severity)
? (errors[fieldId][last].severity = options.severity)
: (errors[fieldId][last].severity = severity);
};
each(validation, (validationList, fieldId) => {
if (isArray(validationList)) {
each(validationList, options => {
const isValid =
isFunction(presets[options.type]) &&
presets[options.type](fieldId, values, options, dispatch);
if (isPromise(isValid)) {
promiseList.push(
new Promise(resolve => {
isValid.then(
resp => {
each(resp && resp.message, m => {
addError(fieldId, m, options);
});
resolve();
},
() => {
resolve();
}
);
})
);
} else if (!isValid) {
addError(fieldId, {}, options);
}
});
}
});
return Promise.all(promiseList).then(() => {
const messagesAction = compact(
map(errors, (messages, fieldId) => {
if (!isEmpty(messages)) {
const message = findPriorityMessage(messages);
if (
!isEqual(message, get(registeredFields, [fieldId, 'message'])) ||
!get(fields, [fieldId, 'touched'])
) {
return addFieldMessage(formName, fieldId, message, isTouched);
}
}
})
);
messagesAction && dispatch(batchActions(messagesAction));
return hasError(errors);
});
};