要点
- Form、FormItem的数据通信
- Form的表单校验
- 表单提交
设计思路
Form用于管理form表单数据,配置校验规则。 FormItem用于添加具体表单项的数据绑定。
vue
<!-- Form 使用 -->
<el-form ref="formRef" :model="formData" :rules="formRules">
<el-form-item prop="username">
<el-input v-model="formData.username" />
</el-form-item>
</el-form>
Form、FormItem的数据通信
插槽
通过插槽(slot
)机制,支持自定义表单项的内容
数据绑定
v-model实现form的双向绑定;
数据通信
provide、inject实现Form向FormItem传递form实例、校验规则
Form组件内存储所有FormItem实例,回传方法通过provide传入
js
// Form 组件
export default defineComponent({
name: 'ElForm',
setup(props, { expose }) {
const fields = ref<FormItemContext[]>([]); // 存储所有 FormItem 实例
// 向子组件提供 Form 实例
provide('formRef', {
registerField: (field: FormItemContext) => {
fields.value.push(field);
},
unregisterField: (field: FormItemContext) => {
fields.value = fields.value.filter(f => f !== field);
},
model: props.model, // 表单数据对象
rules: props.rules, // 校验规则
});
}
});
FormItem校验规则
Element Plus 基于 async-validator
库实现校验逻辑
js
// FormItem 组件
export default defineComponent({
name: 'ElFormItem',
props: {
prop: String, // 绑定到 model 的字段名
rules: [Object, Array] // 字段专属校验规则(覆盖全局)
},
setup(props) {
const form = inject('formRef'); // 获取 Form 实例
const errorMessage = ref('');
// 校验逻辑
const validateField = () => {
const rules = props.rules || form.rules?.[props.prop];
const validator = new AsyncValidator({
[props.prop]: rules
});
return validator.validate({ [props.prop]: form.model[props.prop] })
.then(() => {
errorMessage.value = '';
})
.catch(({ errors }) => {
errorMessage.value = errors[0].message;
return Promise.reject(errors);
});
};
// 注册到 Form 的校验队列
onMounted(() => {
form?.registerField({
validate: () => validateField(),
});
});
return { errorMessage };
}
});
全局校验
由于上述 FormItem校验规则 中已将校验方法回存至 Form 中,因此全局校验只需遍历执行各个 FormItem 中的校验方法即可
js
// Form 组件
export default defineComponent({
name: 'ElForm',
setup(props, { expose }) {
const fields = ref<FormItemContext[]>([]); // 存储所有 FormItem 实例
// 向子组件提供 Form 实例
provide('formRef', {
registerField: (field: FormItemContext) => {
fields.value.push(field);
},
unregisterField: (field: FormItemContext) => {
fields.value = fields.value.filter(f => f !== field);
},
model: props.model, // 表单数据对象
rules: props.rules, // 校验规则
});
// 全局校验
const validateForm = () => {
fields.value.forEach(formItem => {
formItem?.validate && formItem?.validate();
})
};
// 暴露校验方法
defineExpose({
validate: validateForm,
});
}
});
表单提交
对form的submit事件监听器
js
const fields = ref<FormItemContext[]>([]); // 存储所有 FormItem 实例
// 向子组件提供 Form 实例
provide('formRef', {
registerField: (field: FormItemContext) => {
fields.value.push(field);
},
unregisterField: (field: FormItemContext) => {
fields.value = fields.value.filter(f => f !== field);
},
model: props.model, // 表单数据对象
rules: props.rules, // 校验规则
});
// 全局校验
const validateForm = (cb) => {
let promisAll = [];
promisAll = fields.value.filter(formItem => {
return typeof formItem?.validate === 'function';
}).map(formItem => formItem.validate());
Promise.all(promisAll).then((res) => {
let rej = res.filter(item => item);
cb && cb(!rej.length);
})
};
// 暴露校验方法
defineExpose({
validate: validateForm,
});
const formEl = ref(null); // 表单元素的引用
const handleSubmit = (event) => {
event.preventDefault(); // 阻止默认提交行为
validateForm.value((valid) => {
if (valid) {
emit('submit', formModel); // 触发用户定义的 submit 事件
} else {
console.log('Form validation failed');
}
});
};
onMounted(() => {
if (formEl.value) {
formEl.value.addEventListener('submit', handleSubmit);
}
});
onBeforeUnmount(() => {
if (formEl.value) {
formEl.value.removeEventListener('submit', handleSubmit);
}
});