前端小伙伴们在做项目时或多或少都接触过表单,表单最重要的一环就是表单验证,不知道大家考虑过这个问题没有,当使用自己开发的组件时如何触发表单验证提示呢?
我之前做过一个功能,使用的是yamlEditor组件,由于项目(技术栈:vue+element)紧急,当时剑走偏锋,在form-item里同时写了个textarea,与ymalEditor绑定同一个值,这样就能触发form的验证了,具体代码如下:
<el-form-item
:label="$t('label.emsRelease.compose')"
prop="compose"
class="check-box__form-item"
v-if="taskTemplateInfo.taskArgs.deployType == COMPOSE_DEPLOY"
>
<!-- do not delete the code behind -->
<el-input
type="textarea"
:placeholder="$t('placeholder.emsRelease.compose')"
v-model="taskTemplateInfo.taskArgs.compose"
name="compose"
style="position: absolute; z-index: -100"
></el-input>
<yamlEditor
v-model="taskTemplateInfo.taskArgs.compose"
style="width: 400px"
/>
</el-form-item>
后来想想此做法并没有从根源是解决问题,因此,我又到element源码里偷学了,看了一下几个文件
1、node_modules/element-ui/packages/input/src/input.vue
handleBlur(event) {
this.focused = false;
this.$emit('blur', event);
if (this.validateEvent) {
this.dispatch('ElFormItem', 'el.form.blur', [this.value]);
}
},
value(val) {
this.$nextTick(this.resizeTextarea);
if (this.validateEvent) {
this.dispatch('ElFormItem', 'el.form.change', [val]);
}
},
this.dispatch('ElFormItem', 'el.form.blur', [this.value]);
this.dispatch('ElFormItem', 'el.form.change', [val]);
这两代码看起来有点熟悉,然后我就去看了一下dispatch方法。
2、node_modules/element-ui/src/mixins/emitter.js
dispatch(componentName, eventName, params) {
var parent = this.$parent || this.$root;
var name = parent.$options.componentName;
while (parent && (!name || name !== componentName)) {
parent = parent.$parent;
if (parent) {
name = parent.$options.componentName;
}
}
if (parent) {
parent.$emit.apply(parent, [eventName].concat(params));
}
},
这是触发父组件里的某个方法用的,然后我就去寻踪追影,查看form-item的代码
3、node_modules/element-ui/packages/form/src/form-item.vue
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);
}
},
onFieldBlur() {
this.validate('blur');
},
onFieldChange() {
if (this.validateDisabled) {
this.validateDisabled = false;
return;
}
this.validate('change');
},
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);
});
}
这里大概就是在form-item中绑定了el.form.blur和el.form.change两个方法,在触发这两个方法时会执行validate方法,然后完成验证。
到这里我就大概知道该怎么改造自己的组件了,我组件里加了两个方法,如下图:
dispatch(componentName, eventName, params) {
var parent = this.$parent || this.$root;
var name = parent.$options.componentName;
while (parent && (!name || name !== componentName)) {
parent = parent.$parent;
if (parent) {
name = parent.$options.componentName;
}
}
if (parent) {
parent.$emit.apply(parent, [eventName].concat(params));
}
},
//在触发change的地方增加dispatch
checkValue() {
this.$emit("change", this.checkValue);
this.dispatch("ElFormItem", "el.form.change", [this.checkValue]);
},
如果自定义组件可以触发blur事件的话还可以加上
this.dispatch('ElFormItem', 'el.form.blur', [this.checkValue]);
当然样式上也要做下修改,在校验不通过时添加样式,示例如下:
//less
.el-form-item.is-error {
//添加自己的样式
.ci-emp-dropdown {
ci-dropdown__link {
border-color: #ec4c47;
}
}
}