封装表单
首先就是对表单元素的封装,需要考虑两个点,第一是对input、select、radio等元素都支持,并且支持插槽slot。
基于这几点出发,我们就开始考虑需要用到什么支持。这里是用到我自己经常用到的vue框架和element-plus以及typescript提供类型检查,来加强代码健壮性。
开始设计之前,我们需要明白这个组件的实际应用场景,我们需要得到一个可实现数据可控的元素可控的以及自定义性高点的,意味着我们需要利用父子组件传值以及可以获取组件实例。
那么我们就开始设计吧!
首先我们面临一个决策:是使用动态组件标签还是直接使用element-plus标签来生成表单呢?
坚决采用动态组件来生成哦!因为第二种方法会导致代码量增大,并且需要v-if、v-else-if等判断增多来实现生成我们需要的标签,增加使用者的心智负担。
例如:
<component v-if="['el-input', 'el-select'].includes(item.comType)" :is="`${item.comType}`"
v-model="formData[item.key]" :placeholder="item.placeholder">
<template v-if="item.comType === 'el-select'">
<el-option v-for="opt in item.options" :key="opt.value" :value="opt.value"
:label="opt.label"></el-option>
</template>
</component>
可以看到这个动态组件的组成,就如之前所说的未来支持插槽,所有不能将所有el-form-item的都设置为动态组件,我们看到首先判断了它是input的还是select的,通过is将该组件渲染成对应的comtype,绑定双向数据绑定,设置了默认的一个展示数据样式。
然后就是要对select进行判断是否需要el-option标签了,对应的就如以上所写,在以上都完成;那么回到开始的问题,我们需要插槽来放我们自定义的组件。
对于这个slot是一个以及明确的标签所以我们不需要使用动态组件了,避免浪费性能。
<slot v-if="`${item.comType}` === 'slot'" :name="`${item.key}`"></slot>
可以看到这样就可以设置我们需要的自定义内容了,加一个name是因为我们需要给不同的插槽起名加一个标识。
以上会根据我们给出的配置项的内容,来生成我们想要的表单。ok,到这主体内容部分以及完成,那么我们el-form表单如何处理呢?
我们的表单作为最外围标签,它只需要用来获取最终的表单数据,以及提供一个表单实例,我们可以利用表单实例来实现表单校验和清空表单的功能。
那么如何实现就如:
<el-form ref="formRef" :model="formData" :inline="isinline" :size="issize" :rules="isrule">
<template v-for="item in fieldColums" :key="item.key">
<el-form-item :prop="item.key" :label="item.label">
<slot v-if="`${item.comType}` === 'slot'" :name="`${item.key}`"></slot>
<component v-if="['el-input', 'el-select'].includes(item.comType)" :is="`${item.comType}`"
v-model="formData[item.key]" :placeholder="item.placeholder">
<template v-if="item.comType === 'el-select'">
<el-option v-for="opt in item.options" :key="opt.value" :value="opt.value"
:label="opt.label"></el-option>
</template>
</component>
</el-form-item>
</template>
</el-form>
请自动忽略前面讲过的部分,ref就是用来获取组件实例的,model是用来获取表单数据注意是响应式数据,inline其实就是行内表单模式,可以忽略,其实是为了满足我自己开发时需要时加上的,size就是表单内元素大小。
以上这些数据从哪来?还有我们的健壮性如何体现?
以上这些数据从父组件传值过来,包括配置项信息:
const { fieldColums, isinline, issize, isrule } = defineProps<{
fieldColums: Array<FormConfig>;//配置项信息
isinline?: boolean;
issize?: any;
isrule?: any;
}>();
通过defineprops函数来进行父传子值,而健壮性就通过typescript来写一个声明文件:
export type CompType = "el-input" | "el-select" | "slot";
export type SelectOptions = {
label: string;
value: string | number;
};
export type FormConfig = {
key: string;//item对应的key
label?: string;//label文案
valueType?: "string" | "number";//传值类型
comType: CompType;//组件类型
placeholder?: string,//指定默认内容
options?: Array<SelectOptions>;//下拉框选择项
};
这个里面的类型FormConfig就是要约束fieldColums类型的,那么到这我们需要思考到一个问题,在这个组件的数据为我们要如何获取并且处理它,涉及到表单的常用的操作就是对表单进行表单验证并同时将formData提供给使用者进行处理,还有就是清空表单的操作。
const emit = defineEmits(["submit", "reset"]);
const onSubmit = () => {
formRef.value?.validate((vaild) => {
if (vaild) {
console.log("Submit!");
emit("submit", formData);
}
});
};
const onReset = () => {
console.log("Reset!");
formRef.value.resetFields();
emit("reset", {});
};
这里必须要提一嘴,我出于我只需要一个查询按钮的操作,我就将这个查询按钮写成一个插槽了,那么面对一个问题,如何才能调用到子组件的这个操作数据的方法呢?
<FormComponent ref="FormComRef" :field-colums="FormCon" :isinline="true" :isrule="rules" @submit="GetFromModel">
<template v-slot:searchbtn>
<el-button type="primary" :icon="Search" click @click="FormComRef.onSubmit()">查询</el-button>
</template>
</FormComponent>
这时就要使用到子组件的实例方法了,获取咱们的组件实例方法就需要在确保咱们的子组件已经暴露该方法,这个必须要明白不然就获取不到该实例方法。
defineExpose({
onSubmit,
onReset,
});
这样在父组件就可以通过:
const FormComRef = ref<InstanceType<typeof FormComponent>>();
const GetFromModel = (formData) => {
console.log(formData);
//这里写获取帖子请求
};
这里先说明一下,点击父组件的查询按钮,直接触发子组件的onSubmit事件,该事件执行emit事件,将数据formData传回来父组件,父组件通过一个事件接受这个值并处理。以上就是对整个表单组件的封装。
最后通过一个配置文件form-config.ts就可以完成渲染了:
import type { FormConfig } from "@components/FormComponent/types";
import type { FormRules } from "element-plus";
//生成表单的配置项
export const FormCon: Array<FormConfig> = [
{
key: "college",
label: "学院",
valueType: "number",
comType: "el-select",
placeholder: "请选择学院",
options: [
{
label: "无",
value: "0"
},
{
label: "计算机与信息安全学院",
value: "1",
},
{
label: "商学院",
value: "2",
},
],
},
{
key: "speciality",
label: "专业",
valueType: "number",
comType: "el-select",
placeholder: "请选择专业",
options: [
{
label: "无",
value: "0"
},
{
label: "软件工程专业",
value: "1",
},
],
},
{
key: "keywords",
label: "资源查询",
valueType: "string",
comType: "el-input",
placeholder: "请输入关键字",
},
{
key: "searchbtn",
comType: "slot",
},
];
//表单验证的规则
export const rules: FormRules = {
keywords: [
{
max: 6, message: '关键字过长了', trigger: 'blur'
},
],
};
如果还有不妥的地方可以指出,并进行修改。