Vue3+FormKit实现嵌套列表表单(无需Repeater组件)
在Vue 3中用FormKit实现嵌套动态表单(无Repeater)
以下是实现支持动态添加孩子、每个孩子动态添加玩具的完整代码,解决你当前嵌套层级无法绑定的问题:
修改后的完整代码
<script setup lang="ts"> import Swal from 'sweetalert2'; import { ref, inject, onMounted } from 'vue'; import { token } from '@formkit/utils'; const lisyConfig = inject("config"); const foundationId = inject("foundationId"); const makeToysRequests = async (fields: any) => { console.log('提交数据:', fields); await new Promise((r) => setTimeout(r, 1000)) alert(JSON.stringify(fields)) } const form = ref(); const formRef = ref(); // 初始化孩子数组,每个孩子自带一个玩具项 const childItems = ref([{ token: token(), requestsPerChild: [{ token: token() }] }]); // 添加孩子 const addChild = () => { childItems.value.push({ token: token(), requestsPerChild: [{ token: token() }] }) } // 移除孩子 const removeChild = (childToken: string) => { Swal.fire({ title: '确定删除吗?', icon: 'warning', showCancelButton: true, confirmButtonText: '确定', cancelButtonText: '取消' }).then((result) => { if (result.isConfirmed) { childItems.value = childItems.value.filter(item => item.token !== childToken); } }); } // 给指定孩子添加玩具 const addToy = (childToken: string) => { const targetChild = childItems.value.find(child => child.token === childToken); if (targetChild) { targetChild.requestsPerChild.push({ token: token() }); } } // 移除指定孩子的某个玩具 const removeToy = (childToken: string, toyToken: string) => { const targetChild = childItems.value.find(child => child.token === childToken); if (targetChild) { targetChild.requestsPerChild = targetChild.requestsPerChild.filter(toy => toy.token !== toyToken); } } const toktok = ref(token()); onMounted(() => { console.info('Form app mounted'); console.info('form config: ', lisyConfig); }) </script> <template> <div class="lisy-form-wrapper"> <FormKit type="form" ref="formRef" v-model="form" form-class="-form form-grid" id="f_form" submit-label="提交" @submit="makeToysRequests" :actions="false" > <!-- 家长信息组 --> <FormKit type="group" name="partner"> <h2>家长信息</h2> <div class="form-grid address-input-group"> <FormKit type="text" name="firstName" label="名字" validation="required" outer-class="form-grid-column-8" /> <FormKit type="text" name="infix" label="中间名" outer-class="form-grid-column-4" /> <FormKit type="text" name="lastName" label="姓氏" validation="required" outer-class="form-grid-column-12" /> </div> </FormKit> <!-- 孩子列表 --> <FormKit type="list" name="childrenList"> <FormKit v-for="(child, childIndex) in childItems" :key="child.token" :name="`childrenList[${childIndex}]`" type="group" > <div class="lisy-form-child"> <!-- 移除孩子按钮 --> <FormKit v-if="childIndex !== 0" type="button" @click="removeChild(child.token)" prefix-icon="trash" input-class="btn outline" wrapper-class="btn__wrapper btn__wrapper--right" > 删除孩子 </FormKit> <!-- 孩子基本信息 --> <FormKit type="radio" name="gender" validation="required" label="性别" :options="{ jongen: '男孩', meisje: '女孩', }" required /> <div class="form-grid"> <FormKit type="text" name="firstName" label="名字" validation="required" outer-class="form-grid-column-5" /> <FormKit type="text" name="infix" label="中间名" outer-class="form-grid-column-2" /> <FormKit type="text" name="lastName" label="姓氏" validation="required" outer-class="form-grid-column-5" /> </div> <FormKit type="date" name="birthdate" label="出生日期" validation="required" /> <!-- 玩具列表(嵌套层级) --> <div class="toy-list"> <h3>孩子的玩具</h3> <FormKit type="list" :name="`requestsPerChild`"> <FormKit v-for="(toy, toyIndex) in child.requestsPerChild" :key="toy.token" :name="`requestsPerChild[${toyIndex}]`" type="group" > <div class="toy-item"> <FormKit v-if="toyIndex !== 0" type="button" @click="removeToy(child.token, toy.token)" prefix-icon="trash" input-class="btn outline small" wrapper-class="btn__wrapper btn__wrapper--right" > 删除玩具 </FormKit> <FormKit type="textarea" name="voorziening" label="玩具描述" validation="required" /> </div> </FormKit> <!-- 添加玩具按钮 --> <FormKit type="button" @click="addToy(child.token)" prefix-icon="add" input-class="btn purple small" wrapper-class="btn__wrapper btn__wrapper--right" > 添加玩具 </FormKit> </FormKit> </div> <hr> </div> </FormKit> <!-- 添加孩子按钮 --> <FormKit type="button" @click="addChild" prefix-icon="add" input-class="btn purple" wrapper-class="btn__wrapper btn__wrapper--right" > 添加孩子 </FormKit> </FormKit> </FormKit> <pre style="font-size: small"> 孩子数量: {{childItems.length}} 表单数据: {{form}} </pre> </div> </template> <style lang="scss"> .toy-list { margin: 1rem 0; padding: 1rem; background: #f5f5f5; border-radius: 4px; } .toy-item { margin-bottom: 1rem; } </style>
关键修改说明
嵌套数据绑定修正:
- 孩子的
group组件使用name="childrenList[${childIndex}]",明确其在表单数组中的位置 - 玩具的
group组件使用name="requestsPerChild[${toyIndex}]",让FormKit自动将玩具数据嵌套到对应孩子的requestsPerChild数组中
- 孩子的
玩具的动态操作:
- 新增
addToy方法,通过孩子的token定位到目标孩子,向其requestsPerChild数组添加新玩具项 - 新增
removeToy方法,同理定位孩子和玩具,过滤移除目标玩具
- 新增
交互优化:
- 移除按钮直接传递token参数,避免通过DOM元素的dataset获取,逻辑更清晰
- 给玩具列表添加简单样式,区分层级
提交表单后,你会得到如下结构的嵌套数据:
{ "partner": { ... }, "childrenList": [ { "gender": "jongen", "firstName": "...", "birthdate": "...", "requestsPerChild": [ { "voorziening": "玩具1描述" }, { "voorziening": "玩具2描述" } ] } ] }
内容的提问来源于stack exchange,提问作者fdverwoerd
相关产品推荐
相关产品推荐

