前言
vue项目中,大量使用element ui组件中的el-form来构建丰富且具有交互性的表单页面(官网:Element - The world's most popular Vue UI framework)。el-form提供了丰富的表单控件,如输入框、下拉框、日期选择器等,并支持表单校验、表单布局等功能。
其中,表单校验功能使用了async-validator,是一个表单异步校验库,用来检查用户输入是否符合要求,比如必填项是否填写等。官网地址:https://www.npmjs.com/package/async-validator,一个翻译过的blog(相比官网有缺失):https://www.cnblogs.com/web-learn/p/16012789.html
需求
需要用户填写一系列人员,不设上限,但是至少填写一个。每个人员信息包含人员名称和电话号码
- 希望页面“人员”前面添加必填项标识
- 希望检查人员姓名和手机号码是否填写,格式是否符合规范,并给出合理提示
解决方案
1、data中使用数组people接收人员信息
frm:{
project: "",
people:[]
}
2、template中
2.1 el-form-item直接绑定people
prop="people"
2.2 为整个数组设置rules:
:rules="[message: '请添加人员',trigger: 'blur',required: true}]"
2.3 为数组内的成员对象设置rules:
对于数组中的两个字段,分别设置它们对应的rule,这里要注意,因为用了v-for,为了使检查时能够非常清晰明确地知道是哪一行检查失败,prop需要使用变量::prop="`people[${index}].nickName`"
其中,
- prop前面添加冒号:,告知后面的不是普通的字符串,而是表达式;
- 表达式使用``符号(不是单引号,而是键盘左上角的反引号,类似于linux中的执行标记吧
- 变量index使用${}传递
js中,${}是字符串占位符,被称为模板字符串或者模板字面量。通过在 ${} 中放置变量名或表达式,可以将其值插入到字符串中。
<el-form ref="frm" :model="frm" :rules="rules" label-width="80px">
<el-form-item label="人员" prop="people"
:rules="[message: '请添加人员',trigger: 'blur',required: true}]">
<el-row v-for="(row, index) in frm.people" :key="index">
<el-col :span="10">
<el-form-item :prop="`people[${index}].nickName`" :rules="[{required: true,message: '人员姓名不能为空',trigger: 'blur'}]">
<el-input v-model="frm.people[index].nickName" placeholder="请输入人员姓名"/>
</el-form-item>
</el-col>
<el-col :span="10">
<el-form-item :prop="`people[${index}].phonenumber`" :rules="[{required: true,message: '手机号码不能为空',trigger: 'blur'}]">
<el-input v-model="frm.people[index].phonenumber" placeholder="请输入手机号码"/>
</el-form-item>
</el-col>
</el-form-item>
...
</el-form>
半失败的优化尝试
解决方案中,将rules直接写在template中,不满足VUE规范中要求template简洁的要求,所以我尝试了将rules像常规一样集中写在data中,如下:
rules: {
people:[
{
message: '请添加人员',
trigger: 'blur',
required: true
}
],
nickName:[{required: true,message: '人员姓名不能为空',trigger: ['blur','change']}],
phonenumber:[{required: true, pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: '请输入正确的手机号码'}]
},
然后,在template的el-form-item中移除rules:
<el-form ref="frm" :model="frm" :rules="rules" label-width="80px">
<el-form-item label="人员" prop="people">
<el-row v-for="(row, index) in frm.people" :key="index">
<el-col :span="10">
<el-form-item :prop="`people[${index}].nickName`">
<el-input v-model="frm.people[index].nickName" placeholder="请输入人员姓名"/>
</el-form-item>
</el-col>
<el-col :span="10">
<el-form-item :prop="`people[${index}].phonenumber`">
<el-input v-model="frm.people[index].phonenumber" placeholder="请输入手机号码"/>
</el-form-item>
</el-col>
</el-form-item>
...
</el-form>
然后发现最外层people数组校验没有问题,但是里面的人员姓名和电话号码校验失败。又根据官网重新修改rules:
devs:[
{
type: 'array',
required: true,
defaultField: {
type:'object',
required: true,
fields: {
nickName:[{required: true,message: '人员姓名不能为空',trigger: ['blur','change']}],
phonenumber:[{required: true, pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: '请输入正确的手机号码'}]
},
},
}
]
可以校验,但是,它无法准确对标是哪一行,而且,两个字段nickName和phonenumber是顺序校验的,比如,姓名和电话都没有填写,它只是提示姓名不能为空,等我填写了姓名之后,才会提示电话不正确。。。。所以,最终又把这两个字段的校验放回到template中实现了。
总结
1、在el-form中指定rule,可引用data或compute中集中定义的rules,也可以每一条item中单独定义自己的rule
2、另外,花费1天的时间买来的教训:如果rules集中写,在el-form中已经引用了,那么在el-form-item中不能重复引用,否则console中不会报错,但是,rules将不起作用,且提示 people is not a string,即便在rules中添加了 type: 'array' 也不行。
如下写法是错误的。