/
githubmirror
/
element
Обзор
Документация
Войти
/
githubmirror
/
element
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
dev
packages/form/src/form-item.vue
324 строки
9 KB
bofeng
Form-item: rules validate sync (#21892)
02 июн 2022, 12:27
Не верифицирован
02 июн 2022, 12:27
a2b7304
Код
Авторство
О чём код?
<template> <div class="el-form-item" :class="[{ 'el-form-item--feedback': elForm && elForm.statusIcon, 'is-error': validateState === 'error', 'is-validating': validateState === 'validating', 'is-success': validateState === 'success', 'is-required': isRequired || required, 'is-no-asterisk': elForm && elForm.hideRequiredAsterisk }, sizeClass ? 'el-form-item--' + sizeClass : '' ]"> <label-wrap :is-auto-width="labelStyle && labelStyle.width === 'auto'" :update-all="form.labelWidth === 'auto'"> <label :for="labelFor" class="el-form-item__label" :style="labelStyle" v-if="label || $slots.label"> <slot name="label">{{label + form.labelSuffix}}</slot> </label> </label-wrap> <div class="el-form-item__content" :style="contentStyle"> <slot></slot> <transition name="el-zoom-in-top"> <slot v-if="validateState === 'error' && showMessage && form.showMessage" name="error" :error="validateMessage"> <div class="el-form-item__error" :class="{ 'el-form-item__error--inline': typeof inlineMessage === 'boolean' ? inlineMessage : (elForm && elForm.inlineMessage || false) }" > {{validateMessage}} </div> </slot> </transition> </div> </div> </template> <script> import AsyncValidator from 'async-validator'; import emitter from 'element-ui/src/mixins/emitter'; import objectAssign from 'element-ui/src/utils/merge'; import { noop, getPropByPath } from 'element-ui/src/utils/util'; import LabelWrap from './label-wrap'; export default { name: 'ElFormItem', componentName: 'ElFormItem', mixins: [emitter], provide() { return { elFormItem: this }; }, inject: ['elForm'], props: { label: String, labelWidth: String, prop: String, required: { type: Boolean, default: undefined }, rules: [Object, Array], error: String, validateStatus: String, for: String, inlineMessage: { type: [String, Boolean], default: '' }, showMessage: { type: Boolean, default: true }, size: String }, components: { // use this component to calculate auto width LabelWrap }, watch: { error: { immediate: true, handler(value) { this.validateMessage = value; this.validateState = value ? 'error' : ''; } }, validateStatus(value) { this.validateState = value; }, rules(value) { if ((!value || value.length === 0) && this.required === undefined) { this.clearValidate(); } } }, computed: { labelFor() { return this.for || this.prop; }, labelStyle() { const ret = {}; if (this.form.labelPosition === 'top') return ret; const labelWidth = this.labelWidth || this.form.labelWidth; if (labelWidth) { ret.width = labelWidth; } return ret; }, contentStyle() { const ret = {}; const label = this.label; if (this.form.labelPosition === 'top' || this.form.inline) return ret; if (!label && !this.labelWidth && this.isNested) return ret; const labelWidth = this.labelWidth || this.form.labelWidth; if (labelWidth === 'auto') { if (this.labelWidth === 'auto') { ret.marginLeft = this.computedLabelWidth; } else if (this.form.labelWidth === 'auto') { ret.marginLeft = this.elForm.autoLabelWidth; } } else { ret.marginLeft = labelWidth; } return ret; }, form() { let parent = this.$parent; let parentName = parent.$options.componentName; while (parentName !== 'ElForm') { if (parentName === 'ElFormItem') { this.isNested = true; } parent = parent.$parent; parentName = parent.$options.componentName; } return parent; }, fieldValue() { const model = this.form.model; if (!model || !this.prop) { return; } let path = this.prop; if (path.indexOf(':') !== -1) { path = path.replace(/:/, '.'); } return getPropByPath(model, path, true).v; }, isRequired() { let rules = this.getRules(); let isRequired = false; if (rules && rules.length) { rules.every(rule => { if (rule.required) { isRequired = true; return false; } return true; }); } return isRequired; }, _formSize() { return this.elForm.size; }, elFormItemSize() { return this.size || this._formSize; }, sizeClass() { return this.elFormItemSize || (this.$ELEMENT || {}).size; } }, data() { return { validateState: '', validateMessage: '', validateDisabled: false, validator: {}, isNested: false, computedLabelWidth: '' }; }, methods: { validate(trigger, callback = noop) { this.validateDisabled = false; const rules = this.getFilteredRule(trigger); if ((!rules || rules.length === 0) && this.required === undefined) { callback(); return true; } this.validateState = 'validating'; const descriptor = {}; if (rules && rules.length > 0) { rules.forEach(rule => { delete rule.trigger; }); } descriptor[this.prop] = rules; const validator = new AsyncValidator(descriptor); const model = {}; model[this.prop] = this.fieldValue; validator.validate(model, { firstFields: true }, (errors, invalidFields) => { this.validateState = !errors ? 'success' : 'error'; this.validateMessage = errors ? errors[0].message : ''; callback(this.validateMessage, invalidFields); this.elForm && this.elForm.$emit('validate', this.prop, !errors, this.validateMessage || null); }); }, clearValidate() { this.validateState = ''; this.validateMessage = ''; this.validateDisabled = false; }, resetField() { this.validateState = ''; this.validateMessage = ''; let model = this.form.model; let value = this.fieldValue; let path = this.prop; if (path.indexOf(':') !== -1) { path = path.replace(/:/, '.'); } let prop = getPropByPath(model, path, true); this.validateDisabled = true; if (Array.isArray(value)) { prop.o[prop.k] = [].concat(this.initialValue); } else { prop.o[prop.k] = this.initialValue; } // reset validateDisabled after onFieldChange triggered this.$nextTick(() => { this.validateDisabled = false; }); this.broadcast('ElTimeSelect', 'fieldReset', this.initialValue); }, getRules() { let formRules = this.form.rules; const selfRules = this.rules; const requiredRule = this.required !== undefined ? { required: !!this.required } : []; const prop = getPropByPath(formRules, this.prop || ''); formRules = formRules ? (prop.o[this.prop || ''] || prop.v) : []; return [].concat(selfRules || formRules || []).concat(requiredRule); }, getFilteredRule(trigger) { const rules = this.getRules(); return rules.filter(rule => { if (!rule.trigger || trigger === '') return true; if (Array.isArray(rule.trigger)) { return rule.trigger.indexOf(trigger) > -1; } else { return rule.trigger === trigger; } }).map(rule => objectAssign({}, rule)); }, onFieldBlur() { this.validate('blur'); }, onFieldChange() { if (this.validateDisabled) { this.validateDisabled = false; return; } this.validate('change'); }, updateComputedLabelWidth(width) { this.computedLabelWidth = width ? `${width}px` : ''; }, addValidateEvents() { const rules = this.getRules(); if (rules.length || this.required !== undefined) { this.$on('el.form.blur', this.onFieldBlur); this.$on('el.form.change', this.onFieldChange); } }, removeValidateEvents() { this.$off(); } }, mounted() { if (this.prop) { this.dispatch('ElForm', 'el.form.addField', [this]); let initialValue = this.fieldValue; if (Array.isArray(initialValue)) { initialValue = [].concat(initialValue); } Object.defineProperty(this, 'initialValue', { value: initialValue }); this.addValidateEvents(); } }, beforeDestroy() { this.dispatch('ElForm', 'el.form.removeField', [this]); } }; </script>