UNPKG

mobx-react-form

Version:
146 lines (131 loc) 4.27 kB
import { forIn, isEmpty } from "lodash"; import FieldInterface from "../models/FieldInterface"; import FormInterface from "../models/FormInterface"; import StateInterface from "../models/StateInterface"; import { ValidationPlugin, ValidationPluginConfig, ValidationPluginConstructor, ValidationPluginInterface, } from "../models/ValidatorInterface"; class DVR<TValidator = any> implements ValidationPluginInterface<TValidator> { promises: Promise<any>[]; config: any; state: StateInterface | null; extend?: (args: { validator: TValidator; form: FormInterface }) => void; validator: TValidator; schema?: any; constructor({ config, state = null, promises = [], }: ValidationPluginConstructor<TValidator>) { this.state = state; this.promises = promises; this.config = config; this.extend = config?.extend; this.validator = config.package; this.extendValidator(); } extendValidator() { if (typeof this.extend === "function") { this.extend({ validator: this.validator, form: this.state!.form, }); } } validate(field: FieldInterface) { const data = this.state!.form.flatMapValues; this.validateFieldAsync(field, data); this.validateFieldSync(field, data); } makeLabels(validation: any, field: FieldInterface) { const labels: Record<string, any> = { [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: FieldInterface, data: any) { const $rules = this.rules(field.rules, "sync"); if (isEmpty($rules[0])) return; const rules = { [field.path ?? ""]: $rules }; const validation = new (this.validator as any)(data, rules); this.makeLabels(validation, field); if (validation.passes()) return; field.invalidate((validation.errors.get(field.path) as any[])[0], false); } validateFieldAsync(field: FieldInterface, data: any) { const $rules = this.rules(field.rules, "async"); if (isEmpty($rules[0])) return; const rules = { [field.path ?? ""]: $rules }; const validation = new (this.validator as any)(data, rules); this.makeLabels(validation, field); const $p = new Promise((resolve: any) => validation.checkAsync( () => this.handleAsyncPasses(field, resolve), () => this.handleAsyncFails(field, validation, resolve) ) ); this.promises.push($p); } handleAsyncPasses(field: FieldInterface, resolve: () => void) { field.setValidationAsyncData(true); resolve(); } handleAsyncFails( field: FieldInterface, validation: any, resolve: () => void ) { field.setValidationAsyncData( false, (validation.errors.get(field.path) as any[])[0] ); this.executeAsyncValidation(field); resolve(); } executeAsyncValidation(field: FieldInterface) { if (field.validationAsyncData.valid === false) { field.invalidate(field.validationAsyncData.message ?? undefined, false, true); } } rules(rules: any, type: "sync" | "async") { const $rules = Array.isArray(rules) ? rules : typeof rules === 'string' ? rules.split("|") : []; const v = new (this.validator as any)(); return $rules.filter(($rule: any) => type === "async" ? v.getRule($rule.split(":")[0])?.async : !v.getRule($rule.split(":")[0])?.async ); } } export default <TValidator = any>( config?: ValidationPluginConfig<TValidator> ): ValidationPlugin<TValidator> => ({ class: DVR<TValidator>, config, });