Vue项目中,如何为遍历生成的form表单设置ref属性
本文案例基于Vue3+Ts+ElementPlus技术栈,介绍如何通过一次点击事件来触发通过循环生成的多个表单校验
·
写在前面
- 在日常开发中,我们常常会通过
ref=变量名
的方式获取组件的实例对象; - 用得最多的莫过于form表单了,本文基于Vue3+TypeScript+ElementPlus的例子介绍一下为多个表单设置ref属性;
常规用法
- 对于单个的表单,我们通过
ref=属性名
的方式就能够轻松获取到表单实例对象,从而使用该对象上的校验表单项的方法,就像下面这样: - 自定义变量
ruleFormRef
赋值给ref
属性,通过ruleFormRef.value
拿到form表单的实例对象,通过调用validate
方法对表单项目进行校验;
问题
- 但是当我们的form表单是动态生成并且存在多个时,这种
ref=变量名
方式就出问题了; - 例如下面的例子,项目可以动态添加多个;但是每个项目的内容是相同的,同时每个项目的必填项都需要填写,这时候我们希望在提交表单的时候通过
validate
方法校验所有项目;于是想当然的和上面例子一样的写法:
- 通过for循环遍历数组,动态生成多个form表单,通过ref拿到表单实例调用
validate
从而触发表单校验; - 但是实际点击提交之后却没有任何反应,断点调试之后发现实例竟是undefined;
问题产生原因以及解决方式
- 其实仔细想问题就出在于我们的表单是for循环动态生成的,但是ref绑定的始终是一个变量,那么当点击提交按钮时;程序也肯定无法判断当前ref绑定的是哪一个form的实例对象;
- 因此解决关键点就是需要给每一个form表单绑定独立的ref属性,所以ref就不能定义为简单的变量了,需要是一个对象或者数组,用来存储所有表单的实例对象,然后在点击提交按钮时遍历所有的实例对象并且逐个调用
validate
方法,从而实现每个表单的独立校验; - 那么这里我以数组举例,写法如下:
- 修改之后,我们再进浏览器,点击提交查看效果,从下图可以看到已经正常校验了;
- 完整代码例子
<template>
<div style="margin: 20px">
<el-button type="primary" size="default" @click="handleAdd">新增</el-button>
<el-button type="primary" @click="submitForm">提交</el-button>
</div>
<div v-for="(ruleForm, index) in state.list" :key="index">
<el-form
:ref="(el: FormInstance) => (ruleFormRefs[index] = el)"
:model="ruleForm"
:rules="rules"
label-width="120px"
class="demo-ruleForm"
:size="formSize"
>
<el-form-item label="名称" prop="name">
<el-input v-model="ruleForm.name" />
</el-form-item>
<el-form-item label="地区" prop="region">
<el-select v-model="ruleForm.region" placeholder="Activity zone">
<el-option label="Zone one" value="shanghai" />
<el-option label="Zone two" value="beijing" />
</el-select>
</el-form-item>
<el-form-item label="地区2" prop="count">
<el-select-v2 v-model="ruleForm.count" placeholder="Activity count" :options="options" />
</el-form-item>
<el-form-item label="时间" required>
<el-col :span="11">
<el-form-item prop="date1">
<el-date-picker
v-model="ruleForm.date1"
type="date"
label="Pick a date"
placeholder="Pick a date"
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-form-item>
</el-form>
<el-divider direction="horizontal" content-position="left"></el-divider>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue';
import type { FormInstance, FormRules } from 'element-plus';
interface RuleForm {
name: string;
region: string;
count: string;
date1: string;
}
const formSize = ref('default');
// const ruleFormRef = ref<FormInstance>();
const ruleFormRefs = ref<FormInstance[]>([]);
const state = reactive<{
list: RuleForm[];
}>({
list: [
{
name: '',
region: '',
count: '',
date1: '',
},
],
});
const rules = reactive({
name: [
{ required: true, message: 'Please input Activity name', trigger: 'blur' },
{ min: 3, max: 5, message: 'Length should be 3 to 5', trigger: 'blur' },
],
region: [
{
required: true,
message: 'Please select Activity zone',
trigger: 'change',
},
],
count: [
{
required: true,
message: 'Please select Activity count',
trigger: 'change',
},
],
date1: [
{
type: 'date',
required: true,
message: 'Please pick a date',
trigger: 'change',
},
],
});
const handleAdd = () => {
state.list.push({
name: '',
region: '',
count: '',
date1: '',
});
};
const submitForm = async () => {
ruleFormRefs.value?.forEach((ele) => {
ele.validate((val) => {
console.log(val);
});
});
// await ruleFormRef.value?.validate((valid, fields) => {
// if (valid) {
// console.log('submit!');
// } else {
// console.log('error submit!', fields);
// }
// });
};
// const resetForm = (formEl: FormInstance | undefined) => {
// if (!formEl) return;
// formEl.resetFields();
// };
const options = Array.from({ length: 10000 }).map((_, idx) => ({
value: `${idx + 1}`,
label: `${idx + 1}`,
}));
</script>
写在最后
- 另外值得注意的是,以上例子只是一个简单的测试用例,细节的地方没有过多处理;
- 如果在实际开发中,这种新增往往伴随着删除,修改等操作;用数组下标作为for循环的key值不可取,应该使用其他唯一标识的字段;同时用于存储form实例的缓存可以定义为对象,通过key-value的形式进行存储,当然如果每个项目的key值刚好是number类型的话,定义成数组就行。
更多推荐
已为社区贡献2条内容
所有评论(0)