Vue3 动态生成表单元素时数据绑定与值存储的实现方案
问题原因
- v-model语法错误:模板中给
v-model加了:前缀(即:v-model),这会被解析为动态绑定v-model属性,而非启用Vue的双向绑定语法,输入内容完全不会同步到关联变量。 - contentArray初始化不灵活:当前写死了长度为9的初始数组,若传入的
content配置项数量超过9,后续的表单项会绑定到不存在的数组下标,也会出现绑定失效。 - v-for的key值不规范:直接用
item对象作为key,若存在重复配置项会导致渲染异常,建议用配置项的唯一标识或者下标作为key。
修复方案
第一步:修正模板语法
删除所有v-model前的冒号,调整v-for的key:
<div v-for="(item, index) in content" :key="index"> <template v-if="item.type == 'inputField'" style="margin-top: 1rem;"> <label>{{ item.label }}</label> <input type="text" v-model="contentArray[index]" placeholder="Enter text" /> </template> <template v-if="item.type == 'textArea'" style="margin-top: 1rem;"> <label>{{ item.label }}</label> <textarea name="textarea" v-model="contentArray[index]" cols="10" rows="10" ></textarea> </template> </div> <button label="submit" @click="getAllData" />
第二步:修正setup逻辑
根据传入的配置动态初始化contentArray,无需写死长度:
setup(props) { const displayDialog = ref(false); // 按传入的content长度动态初始化 const contentArray = ref(props.content.map(() => null)); const open = () => { displayDialog.value = true; }; const close = () => { displayDialog.value = false; // 可选:关闭弹窗时清空表单内容 contentArray.value = props.content.map(() => null); }; const getAllData = () => { console.log(contentArray.value); } return { displayDialog, open, close, getAllData, contentArray } }
更优实现方案
当前用数组存储表单值,使用时需要手动对应下标和表单项的关系,维护成本高。推荐给每个配置项增加field字段,用对象存储表单值,提交时直接返回键值对,使用更便捷:
调整组件使用方式
<ReusableDialog :content="[ {label: '用户名', type: 'inputField', field: 'username'}, {label: '个人简介', type: 'textArea', field: 'bio'} ]" />
调整组件逻辑
setup(props) { const displayDialog = ref(false); // 用reactive对象存储表单键值对 const formData = reactive( props.content.reduce((prev, curr) => { prev[curr.field] = null return prev }, {}) ) const open = () => { displayDialog.value = true; }; const close = () => { displayDialog.value = false; Object.keys(formData).forEach(key => formData[key] = null) }; const getAllData = () => { // 直接返回键值对结构,无需对应下标 console.log({...formData}) } return { displayDialog, open, close, getAllData, formData } }
调整模板绑定
<input type="text" v-model="formData[item.field]" placeholder="Enter text" /> <!-- textarea绑定逻辑同理 --> <textarea v-model="formData[item.field]" cols="10" rows="10" ></textarea>
内容的提问来源于stack exchange,提问作者Nora
相关产品推荐
相关产品推荐

