前言
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:
people:[ { 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' 也不行。
如下写法是错误的。