form表单验证及相关拓展(动态验证)
一、前言
最近遇到两个需求,一个是需要对一个表格动态生成的表单项进行校验,还有一个是对动态生成的多个表单进行校验。关于表单的动态校验,这个elementui文档上也有说明传送任意门,其他拓展的没有详细讲解,所以本篇随笔主要是对表单校验进行一个总结,以便后续回顾。
二、普通的表单验证

这种表单验证官方文档上写的很详细, 只需注意将Form-Item 的 prop 属性设置为需校验的字段名即可。
rules: { name: [ { required: true, message: '请输入活动名称', trigger: 'blur' }, { min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' } ], region: [ { required: true, message: '请选择活动区域', trigger: 'change' } ], date1: [ { type: 'date', required: true, message: '请选择日期', trigger: 'change' } ], date2: [ { type: 'date', required: true, message: '请选择时间', trigger: 'change' } ], type: [ { type: 'array', required: true, message: '请至少选择一个活动性质', trigger: 'change' } ], resource: [ { required: true, message: '请选择活动资源', trigger: 'change' } ], desc: [ { required: true, message: '请填写活动形式', trigger: 'blur' } ] }

如果默认的校验规则满足不了需求还可以通过validator函数自定义验证规则,例如:
1 export default { 2 data() { 3 var checkAge = (rule, value, callback) => { 4 if (!value) { 5 return callback(new Error('年龄不能为空')); 6 } 7 setTimeout(() => { 8 if (!Number.isInteger(value)) { 9 callback(new Error('请输入数字值')); 10 } else { 11 if (value < 18) { 12 callback(new Error('必须年满18岁')); 13 } else { 14 callback(); 15 } 16 } 17 }, 1000); 18 }; 19 var validatePass = (rule, value, callback) => { 20 if (value === '') { 21 callback(new Error('请输入密码')); 22 } else { 23 if (this.ruleForm.checkPass !== '') { 24 this.$refs.ruleForm.validateField('checkPass'); 25 } 26 callback(); 27 } 28 }; 29 var validatePass2 = (rule, value, callback) => { 30 if (value === '') { 31 callback(new Error('请再次输入密码')); 32 } else if (value !== this.ruleForm.pass) { 33 callback(new Error('两次输入密码不一致!')); 34 } else { 35 callback(); 36 } 37 }; 38 return { 39 ruleForm: { 40 pass: '', 41 checkPass: '', 42 age: '' 43 }, 44 rules: { 45 pass: [ 46 { validator: validatePass, trigger: 'blur' } 47 ], 48 checkPass: [ 49 { validator: validatePass2, trigger: 'blur' } 50 ], 51 age: [ 52 { validator: checkAge, trigger: 'blur' } 53 ] 54 } 55 }; 56 },
以下几点需要注意:
- 自定义校验规则中不能直接写return,比如
if(!value)return;必须返回一个回调函数callback()(返回callback的入参为空代表校验通过规则,返回含new Error(‘自定义提示’)入参代表校验不通过规则。) - 必须保证自定义校验规则的每个分支都调用了callback方法,否则会导致el-form组件的validate方法无法进入回调函数,就会提交失败。
1 submitForm(formName) { 2 //提交表单进行保存校验 3 this.$refs[formName].validate((valid) => { 4 if (valid) { 5 alert('submit!'); 6 } else { 7 console.log('error submit!!'); 8 return false; 9 } 10 }); 11 },
二、表格动态添加表单校验

上面动图为表格内嵌入表单动态添加校验,需要注意的点:动态添加的表单项绑定的prop属性需要用scope.$index区分绑定值,否则会导致每一行的绑定值都是一样的;
HTML代码
<el-table :data="dataForm.transferPlanDetails"
style="width: 100%;padding:0"
stripe
border
fit
class="tabletree"
>
<el-table-column
label="资产类别"
min-width="180"
:align="DefaultAlignCommon"
prop="assetCategoryId"
show-overflow-tooltip>
<template slot-scope="scope">
<el-form-item
:prop="'transferPlanDetails.' + scope.$index + '.assetCategoryId'"
:rules="[{required: true, message: '请选择资产类别', trigger: ['blur','change']}]"
>
<treeselect
noResultsText="无匹配选项"
noOptionsText="暂无数据"
append-to-body
z-index="9998"
:clearOnSelect="true"
:show-count="true"
:searchable="true"
filter-node-method="filterNode"
:options="assetCategoryTree"
:normalizer="normalizerAsset"
:default-expand-level="1"
placeholder="全部"
v-model="scope.row.assetCategoryId"
@select="handleAssetCategoryId"/>
</el-form-item>
</template>
</el-table-column>
<el-table-column
label="品牌"
min-width="180"
:align="DefaultAlignCommon"
prop="brand"
>
<template slot-scope="scope">
<!-- <el-select v-model="scope.row.brand" @focus="getDetail(scope.row.assetCategoryId)" clearable placeholder="全部">
<el-option v-for="(obj,index) in brandData" :key="index"
:label="obj" :value="obj"></el-option>
</el-select> -->
<el-form-item
:prop="'transferPlanDetails.' + scope.$index + '.brand'"
:rules="[{required: true, message: '请选择品牌', trigger: ['blur','change']}]"
>
//封装的下拉框组件,用来实现与资产类别的下拉框内容联动
<unique-select :key="scope.$index"
:queryName ='scope.row.assetCategoryId'
v-model='scope.row.brand'
/>
</el-form-item>
</template>
</el-table-column>
<el-table-column
label="规格型号"
min-width="180"
:align="DefaultAlignCommon"
show-overflow-tooltip
prop="specificationModel">
<template slot-scope="scope">
<el-form-item
:prop="'transferPlanDetails.' + scope.$index + '.specificationModel'"
:rules="rules.specificaValid"
>
<el-input v-model="scope.row.specificationModel" clearable></el-input>
</el-form-item>
</template>
</el-table-column>
<el-table-column
label="数量"
min-width="120"
:align="DefaultAlignCommon"
show-overflow-tooltip
prop="quantity">
<template slot-scope="scope">
<el-form-item
:prop="'transferPlanDetails.' + scope.$index + '.quantity'"
:rules="rules.quantityValid"
>
<el-input v-model="scope.row.quantity" clearable></el-input>
</el-form-item>
</template>
</el-table-column>
<el-table-column label= "操作" :align="DefaultAlignUnique" min-width="150">
<template slot-scope="scope">
<el-button type="text" size="mini" @click="addRow(scope.row)" class='el-icon-circle-plus' ></el-button>
<el-button type="text" size="mini" @click="deleteRow(scope.$index)" class='el-icon-remove' ></el-button>
</template>
</el-table-column>
</el-table>
三、多表单的循环校验

需要注意的点:
- el-form标签的ref需使用动态属性,如:ref="'xxx'+i"的形式;
- 在使用validate方法进行校验的时候,$refs[register][0].validate需要加上[0],因为动态生成的表单ref会变成数组,不加[0]会找不到对应的dom树节点。

HTML代码
<div v-for="(o,i) in modal.RowSelection" :key="'call_'+i" style="margin-bottom:10px">
<div class="detail-bor-div">
<table class="unique-detail-table">
<tr>
<td width="10%">资产编号</td> <td width="15%">{{ o.assetLedgerVo.assetCode?o.assetLedgerVo.assetCode:"" }}</td>
<td width="10%">资产名称</td> <td width="15%">{{ o.assetLedgerVo.assetName?o.assetLedgerVo.assetName:"" }}</td>
<td width="10%">S/N号</td> <td width="15%">{{ o.assetLedgerVo.snNum?o.assetLedgerVo.snNum:"" }}</td>
</tr>
</table>
</div>
<div class="detail-bor-div"><br/>
//注意:model绑定的值必须是一个对象
<el-form :ref="'dataForm_mul'+i" :rules="rules" :model="modal.formList[i]" label-position="right"
label-width="36%">
<el-row>
<el-col :span="8">
<el-form-item label="调入后使用人" prop="afterUserId">
<el-select v-model="modal.formList[i].afterUserId" placeholder=" ">
<el-option v-for="(obj,index) in userList" :key="index"
:label="obj.userName" :value="obj.userId"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="调入人" prop="transferInUserId">
<el-select v-model="modal.formList[i].transferInUserId" placeholder=" ">
<el-option v-for="(obj,index) in userList" :key="index"
:label="obj.userName" :value="obj.userId"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="调入日期" prop="transferInTime">
<el-date-picker
v-model="modal.formList[i].transferInTime"
type="date" format="yyyy-MM-dd"
value-format="yyyy-MM-dd"
placeholder="选择日期时间"
:picker-options="pickerOptions"
@change="handleDateSelect">
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="调入后使用部门" prop="transferInDeptId">
<el-select
v-model="modal.formList[i].transferInDeptId"
clearable placeholder=" " filterable
@change="handleSelectDept">
<el-option
v-for="(obj, index) in deptList"
:key="'ut' + index"
:label="obj.organName"
:value="obj.organId"
:disabled="obj.status=='ON'?false:true"
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="调入后存放位置" prop="transferInLocationId" label-width="40%">
<treeselect
noResultsText="无匹配选项"
noOptionsText="暂无数据"
:clearOnSelect="true"
:show-count="true"
:searchable="true"
:flat="true"
filter-node-method="filterNode"
:options="locationTree"
:normalizer="normalizerLocation"
:default-expand-level="99"
placeholder=" "
v-model="modal.formList[i].transferInLocationId"
@select="handleSetLocationName($event)"/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="调入备注" prop="transferInRemark" label-width="12%">
<el-input v-model="modal.formList[i].transferInRemark" maxlength="200" show-word-limit></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<!-- <multitle ref="multitle" :userLists='userList' :deptLists="deptList"/> -->
</div>
</div>
js代码
ok: function () {
let self=this
let data2 = {}
data2.status='mul';
let arr=[];//保存验证结果
let flg = true//
let mulLength = self.modal.RowSelection.length
console.log("refs0",self.$refs['dataForm_mul0']);
console.log("refs1",self.$refs['dataForm_mul1']);
//循环验证每个表单若有一个表单不符合校验标准则提交失败
for(let i=0;i<mulLength;i++){
let register = 'dataForm_mul'+i
self.$refs[register][0].validate((valid) => {
if (valid) {
arr.push('true')
let data = self.modal.formList
self.$set(data2,'transferRecordId',self.modal.RowSelection[0].transferRecordId)
if(i == mulLength-1){
arr.forEach(el=>{
if(el=='false'){
flg = false
}
})
if(flg){
self.$emit('model-ok', {data:data,data2:data2})
}
}
}
else{
arr.push('false')
console.log("error");
return
}
})
}
},

浙公网安备 33010602011771号