UNPKG

mobx-react-form

Version:
158 lines (137 loc) 4.14 kB
import _ from "lodash"; import { ValidationPlugin, ValidationPluginConfig, ValidationPluginConstructor, ValidationPluginInterface, } from "../models/ValidatorInterface"; /** Declarative Validation Rules const plugins = { dvr: dvr({ package: validatorjs, extend: callback, }), }; */ export class DVR implements ValidationPluginInterface { promises = []; config = null; state = null; extend = null; validator = null; constructor({ config, state = null, promises = [], }: ValidationPluginConstructor) { this.state = state; this.promises = promises; this.extend = config?.extend; this.validator = config.package; this.extendValidator(); } extendValidator() { // extend using "extend" callback if (typeof this.extend === 'function') { this.extend({ validator: this.validator, form: this.state.form, }); } } validate(field) { // get form fields data const data = this.state.form.validatedValues; this.validateFieldAsync(field, data); this.validateFieldSync(field, data); } makeLabels(validation, field) { const labels = { [field.path]: field.label }; _.forIn(validation.rules[field.path], (rule) => { if ( typeof rule.value === "string" && rule.name.match(/^(required_|same|different)/) ) { _.forIn(rule.value.split(","), (p, i: any) => { if (!rule.name.match(/^required_(if|unless)/) || i % 2 === 0) { const f = this.state.form.$(p); if (f && f.path && f.label) { labels[f.path] = f.label; } } }); } else if ( typeof rule.value === "string" && rule.name.match(/^(before|after)/) ) { const f = this.state.form.$(rule.value); if (f && f.path && f.label) { labels[f.path] = f.label; } } }); validation.setAttributeNames(labels); } validateFieldSync(field, data) { const $rules = this.rules(field.rules, "sync"); // exit if no rules found if (_.isEmpty($rules[0])) return; // get field rules const rules = { [field.path]: $rules }; // create the validator instance const validation = new this.validator(data, rules); // set label into errors messages instead key this.makeLabels(validation, field); // check validation if (validation.passes()) return; // the validation is failed, set the field error field.invalidate(_.head(validation.errors.get(field.path)), false); } validateFieldAsync(field, data) { const $rules = this.rules(field.rules, "async"); // exit if no rules found if (_.isEmpty($rules[0])) return; // get field rules const rules = { [field.path]: $rules }; // create the validator instance const validation = new this.validator(data, rules); // set label into errors messages instead key this.makeLabels(validation, field); const $p = new Promise((resolve) => validation.checkAsync( () => this.handleAsyncPasses(field, resolve), () => this.handleAsyncFails(field, validation, resolve) ) ); this.promises.push($p); } handleAsyncPasses(field, resolve) { field.setValidationAsyncData(true); resolve(); } handleAsyncFails(field, validation, resolve) { field.setValidationAsyncData(false, _.head(validation.errors.get(field.path))); this.executeAsyncValidation(field); resolve(); } executeAsyncValidation(field) { if (field.validationAsyncData.valid === false) { field.invalidate(field.validationAsyncData.message, false, true); } } rules(rules, type) { const $rules = _.isString(rules) ? _.split(rules, "|") : rules; // eslint-disable-next-line new-cap const v = new this.validator(); return _.filter($rules, ($rule) => type === "async" ? v.getRule(_.split($rule, ":")[0]).async : !v.getRule(_.split($rule, ":")[0]).async ); } } export default (config?: ValidationPluginConfig): ValidationPlugin => ({ class: DVR, config, });