做过几次动态表单,每次都坎坷解决记忆不深刻,问题都出在最终的表单校验上,特此记录!!! 首先要实现如下效果:
需求描述:需要动态添加/删除表单信息,并添加校验,点击提交,校验通过则输出表单内容。
(代码是vue3+elementPlus+ts实现的,但实现思路与vue2都是通用的,代码略长,仅关注重要的地方即可。 elementPlus官方有提供动态表单实例: element-plus.gitee.io/zh-CN/compo… )
- 重点1,
el-form
绑定的model,需要有一个Key,用于prop绑定时对象查找,我把数据放入了dataList.form
中,(重点重点!!!)切不可把dataList作为一个数据去遍历,不然prop绑定的数据无处可寻。 - 重点2,prop绑定的
'form[下标].key'
,不管嵌套几层,遵循这个规则即可,直到一层层追踪到对应的值。请试图根据案例中v-for
遍历,去理解下此处的prop绑定
:prop="`form[${listIndex}].secondList[${reportFormIndex}].second[${index}].secondName`"
- 重点3,确保根据prop绑定的数据,能够找到对应model里面的值
说下自己错误解决思路,以及碰到的问题:
- prop绑定:刚开始以为是该对象对应的字段名即可,然后毫无意外的报错了,
please transfer a valid prop path to form item!
- 那么又想,是不是可以遍历多个
el-form
,然后动态生成ref
作为id,再遍历这多个el-form
,进行校验,如你所想,太复杂,方案太绕了!!!
附最终完整代码:
<template>
<el-form ref="ruleForms" size="small" :model="dataList">
<el-collapse v-model="activeNames">
<el-collapse-item
v-for="(listItem, listIndex) in dataList.form"
:key="listItem.firstTitleId"
:name="listItem.firstTitleId"
>
<template #title>
<h2>{{ listItem.firstTitleName }}</h2>
</template>
<el-card
shadow="never"
v-for="(reportFormItem, reportFormIndex) in listItem.secondList"
:key="reportFormIndex"
>
<template #header>
<div class="spaceBetween">
{{ reportFormItem.secondTitleName }}
<div>
<el-button
type="primary"
size="small"
@click="addFun(reportFormItem)"
>添加变量</el-button
>
</div>
</div>
</template>
<el-empty
:image-size="40"
v-if="
!reportFormItem.second || reportFormItem.second.length == 0
"
></el-empty>
<el-row
:gutter="40"
v-for="(formItem, index) in reportFormItem.second"
:key="index"
>
<el-col :span="9" :offset="2">
<el-form-item
placeholder="请输入名称"
:prop="`form[${listIndex}].secondList[${reportFormIndex}].second[${index}].secondName`"
label="名称"
:rules="[
{
required: true,
message: '请输入名称',
trigger: 'change',
},
]"
>
<el-input v-model="formItem.secondName"></el-input>
</el-form-item>
</el-col>
<el-col :span="4">
<el-link
type="primary"
@click="
deleteFun(
index,
listItem.firstTitleId,
reportFormItem.secondTitleId,
formItem.secondId,
reportFormItem.second
)
"
>删除</el-link
>
</el-col>
</el-row>
</el-card>
</el-collapse-item>
</el-collapse>
</el-form>
<div class="textRight">
<el-button @click="submit" size="small" type="primary" class="block"
>提交</el-button
>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs } from "vue";
export default defineComponent({
setup() {
const state = reactive({
ruleForms:null as any,
dataList: {
form: [
{
firstTitleId: "fac12713",
firstTitleName: "一级标题",
secondList: [
{
secondTitleId: "9fad33f5",
secondTitleName: "二级标题",
second: [
{
secondId: "9cdbd43d",
secondName: "新增1",
},
],
},
],
},{
firstTitleId: "fac127132",
firstTitleName: "一级标题2",
secondList: [
{
secondTitleId: "9fad33f5",
secondTitleName: "二级标题2",
second: [],
},
],
},
],
},
activeNames: "fac12713",
});
const deleteFun = (index, firstTitleId, paraId, varId, second) => {
second.splice(index, 1);
};
const addFun = (item) => {
item.second.push({
secondType: "",
secondName: "",
});
};
//保存并下一步
const submit = async () => {
state.ruleForms.validate(async (valid: any) => {
console.log(state.dataList)
});
};
return {
...toRefs(state),
deleteFun,
addFun,
submit
};
},
});
</script>