在使用element-ui开发中,经常会用到对动态创建的表单进行字段验证,比如新增多个类似卡片的表单,里面的字段需要做验证,此时就要使用到动态表单验证,官方文档写的有示例,但是不够清晰。除了官方示例,还可以使用循环生成多个form实现该需求。
第一种官方示例:一个Form多项
下面以实际项目中的代码为例:
<el-form ref="form" :model="form" :rules="rules" label-width="110px" size="small" style="width: 90%;">
<div v-for="(item, index) in form.list" :key="item.id" class="box-card">
<el-form-item :prop=" 'list.' + index + '.maintUnit'" :rules="rules.maintUnit" label="运维单位">
<SearchOpsDepartment :value.sync="item.maintUnit"></SearchOpsDepartment>
</el-form-item>
<el-form-item :prop=" 'list.' + index + '.userId'" :rules="rules.userId" label="运维人员">
<SearchUsers :value.sync="item.userId"></SearchUsers>
</el-form-item>
<div>
<el-button type="text" @click="handleAdd">添加</el-button>
<el-button v-if="index !== 0" type="text" @click="handleDelete(index)">删除</el-button>
</div>
</div>
</el-form>
<script>
data() {
return {
form: {
list: [{ id: Date.now() }] // 这里添加了一个当前时间戳作为循环使用的key,如果使用index作为key,虽然避免的报错,但是对性能没有什么帮助,而且可能增加新能问题。
},
rules: {
maintUnit: [
{
required: true,
message: '运维单位不能为空!',
trigger: ['change', 'blur']
}
],
userId: [
{
required: true,
message: '运维人员不能为空!',
trigger: ['change', 'blur']
}
]
}
}
}
</script>
注意重点
- form表单里的
:model="form" :rules="rules"
model和rules不能省略,而且这个model必须是个对象,虽然在当前例子里,我们的数据form其实就是一个数组,但是还要包裹一层成对象。 - 每一项绑定prop时,一定要按照
:prop=" 'list.' + index + '.maintUnit'"
这种格式,list是需要循环的数组key名,一定不要写成form.list,也不是循环出的每一项item,index是每项的下标,maintUnit是当前字段key值。 - 每一项必须单独绑定验证规则,如例子中的
:rules="rules.maintUnit"
- prop和当前字段的key值要保持一致,无论是动态表单还是静态表单都要保持一致
- 验证规则不能用正则
- 表单验验证时,一定要有默认值。比如
form
里的input
绑定的是v-model="form.value"
,data
里定义form
时一定要要加value
,如form: { value: '' }
第二种:多个form
<CardBox v-for="(item, index) in list" :key="index" :title="item.itemName">
<el-form ref="form" :model="item" class="size-auto width-100" :rules="rules" :inline="true" size="medium" label-width="120px">
<el-form-item label="计划工程量" prop="workAmount">
<el-input v-model.trim="item.workAmount" maxlength="15" clearable placeholder="支持小数点后2位" @change="handleComputed(item, index)" />
</el-form-item>
<el-form-item label="单价" prop="unitPrice">
<el-input v-model.trim="item.unitPrice" maxlength="50" show-word-limit clearable placeholder="请输入" @change="handleComputed(item, index)" />
</el-form-item>
<el-form-item label="计划完成金额" prop="prepareCompletionMoney" :rules="moneyRules(item.budgetContent.budgetMoney)">
<el-input v-model.trim="item.prepareCompletionMoney" disabled maxlength="15" clearable placeholder="支持小数点后2位">
<i slot="suffix" class="el-input__icon input-unit">元</i>
</el-input>
</el-form-item>
<el-form-item label="工作内容及标准">
<el-input v-model.trim="item.content" type="textarea" maxlength="200" show-word-limit clearable placeholder="请输入" />
</el-form-item>
<el-form-item label="备注">
<el-input v-model.trim="item.remark" type="textarea" maxlength="200" show-word-limit clearable placeholder="请输入" />
</el-form-item>
</el-form>
</CardBox>
<script>
data() {
return {
list: [],
rules: {
workAmount: [{ validator: this.validNum, trigger: 'blur' }],
unitPrice: [{ validator: this.validNum, trigger: 'blur' }],
adjustmentFactor: [{ validator: this.validNum, trigger: 'blur' }]
}
}
},
methods: {
validNum(rule, value, callback) {
if (value && !isNumber2(value)) {
callback(new Error('填写格式错误'))
} else {
callback()
}
},
moneyRules(budgetMoney) {
return [{
validator: (rule, value, callback) => this.validMoney(rule, value, callback, budgetMoney),
trigger: 'change'
}]
},
validMoney(rule, value, callback, budgetMoney) {
if (value) {
if (value > budgetMoney) {
callback(new Error('不能大于预算金额'))
} else {
callback()
}
} else {
callback()
}
},
// 获取多个form验证结果
handleValidate() {
const list = []
const validas = []
this.$refs['form'].forEach((item, index) => {
list.push(new Promise(resolve => {
item.validate(valida => {
validas.push(valida)
resolve()
})
})
)
})
Promise.all([...list]).then(() => {
const res = !validas.some(item => item === false)
return res
}).catch(() => {
return false
})
}
}
</script>
复杂表单的验证
很多时候会遇到其中一个值,需要当前项的某个值做验证,如上第二种表单中,要求计划完成金额不能大于预算量,这里有两种实现方式
1、通过 rule.field获取到当前项下标,只适用第一种动态表单;
validEverydayBudgetAmount(rule, value, callback) {
if (value) {
if (!isNumber2(value)) {
callback(new Error('填写格式错误'))
return
}
// rule.field转为数组后第二个就是下标
const index = rule.field.split('.')[1]
const budgetAmount = this.everydayCost[index].budgetAmount
if (value > budgetAmount) {
callback(new Error(`不能大于总预算量${budgetAmount}`))
} else {
callback()
}
} else {
callback()
}
},
2、把验证规则写在item行内,这样就可以通过传参获取当前项值;
<el-form-item label="计划完成金额" prop="prepareCompletionMoney" :rules="moneyRules(item.budgetContent.budgetMoney)">
// 验证规则见第二种表单的示例代码
获取验证结果
第二种多个form的,验证结果获取较为麻烦,因为每个验证都是异步,这里使用promise方式获取。父组件获取验证结果时,调用子组件内handleValidate方法,通过then或者await获取结果即可。
// 获取多个form验证结果,具体参考第二种form代码
handleValidate() {
const list = []
const validas = []
this.$refs['form'].forEach((item, index) => {
// 把单个验证结果处理为promise函数,然后添加到list中
list.push(new Promise(resolve => {
item.validate(valida => {
validas.push(valida)
resolve()
})
})
)
})
// 通过Promise.all等待几个验证都完成再处理
return Promise.all([...list]).then(() => {
const res = !validas.some(item => item === false)
return res
}).catch(() => {
return false
})
}