以前我写表单验证:一坨坨的
if else
混搭,很混乱,而且很容易出错,如果能像写配置文件那样去处理表单验证那就太棒了。这篇主要讨论表单校验中的 校验器集、值集 以及如何建立关联。
先来看看最常见的做法
# 阴险的 if else ? ↵
00function check(){ 01 var phone = '1xxxxxxxxxx'; // 得到手机 02 var name = 'eczn'; // 得到名字 03 04 if (!/^1(3|5|7|8|9)[0-9]{9}$/.test(phone)){ 05 alert('phone 填写错误'); 06 return; 07 } else if (name.length < 6) { 08 alert('姓名长度不够 6 位'); 09 return; 10 } else if ( /* ..... */ ){ 11 // ...... 12 return; 13 } 14 15 // 把前端验证过的数据发给后台 16 sendJSON({ 17 phone: phone, 18 name: name 19 }); 20} 21 22function sendJSON(data){ 23 // .... post data to backend 24} 25 26// 把check绑定到指定按钮上 27$(selector).click(check);
数据少还不会怎么样,如果有六七条数据就很麻烦。比如注册帐号,不仅要判断各种类型的数据,说不定不同数据间还有依赖 (比如前后两次输入密码要一致、验证码等等),再加上有时候验证又不止一条规则,这样你要写很多
&&
求逻辑与了,这样一行代码很长,很不适合阅读的修改。随之而来的就是一坨坨
if else
层叠 ———— 这可丑极了,而且很明显的:不利于维护修改,也不利于重复利用。总之,离这些阴险的
if else
远点比较好。# 校验器集配置 ↵
很显然 表单处理无非把
val
传进函数 check(val)
用以返回布尔值来表示相反的两个含义:- 返回
true
则代表通过校验 - 返回
false
则代表不能通过校验
check(val) 把 val 映射到数组
[true, false]
上,显然 check
最好是纯函数、不应该有副作用才是最佳。现在来写个对象
checker
来保存这些 check 函数:00var checker = { 01 // val 存在吗?true代表是、肯定, false代表不是、否定 02 exist: function(val){ 03 return !!val; 04 }, 05 // 长度大于等于 n 吗? 06 lengthMoreThan(val, n){ 07 return val.length >= n; 08 }, 09 // 长度小于等于 n 吗? 10 lengthLessThan: function(value, length){ 11 return value.length <= length; 12 }, 13 // 是手机号吗? 14 isPhone: function(tel){ 15 return /^1(3|5|7|8|9)[0-9]{9}$/.test(tel); 16 }, 17 // a 严格等于 b 吗? 18 equal: function(a, b){ 19 return a === b; 20 }, 21 // n 是数字吗? 22 isNumber: function(n){ 23 return /^[0-9]*$/.test(n); 24 } 25}
这样 checker[‘exist’] 就可以得到 exist,从而完成了从字符串到函数的映射。
00var todo = 'isNumber'; 01 02checker[todo]('Hello, Checker'); // => false 03checker[todo]('0'); // => true
Value: String -> Checker: Function
# 值集配置 ↵
需要校验的数值通常不只一个,因此我采用数组去存储这些值。
00var vals = [ phone, name ];
当然这样还远远不够。
配置文件应当、而且是一些参数集合,可以被遍历,被识别处理。
以下 是我个人认为的一种比较好的写法
00var vals = [ 01 { 02 method: 'isPhone', 03 args: [this.userPhone], 04 err: '手机号输入错误' 05 }, 06 { 07 method: 'lengthLessThan', 08 args: [this.userName, 12], 09 err: '用户名不能长于 12 个字' 10 }, 11 { 12 method: 'exist', 13 args: [this.userSex], 14 err: '性别未填写' 15 }, 16 { 17 method: 'lengthMoreThan', 18 args: [this.userPwd, 6], 19 err: '密码不得少于 6 个字符' 20 }, 21 { 22 method: 'equal', 23 args: [this.userPwd, this.userPwd2], 24 err: '前后密码输入不一致' 25 }, 26 { 27 method: 'isNumber', 28 args: [this.userVeri], 29 err: '请输入合法的数字验证码' 30 } 31];
- methods 是方法集,表明必须都通过这些校验。
- args 是执行这些方法的时候 传给这些方法的参数。
- err 是错误提示。
# 遍历vals ↵
00var res = vals.filter(val => { 01 // checker 是校验器集 02 var todo = checker[val.method]; 03 04 // 调用校验器 利用apply把参数传进去 05 // 再把返回结果取反 取得 数组 res 里面存放未通过验证的数据 06 return !todo.apply(this, val.args); 07}); 08 09if (res.length !== 0){ 10 // 说明有数据未通过验证 11 alert(res[0].err); 12}
# 下篇 ↵
- checker 保存校验器
- vals 保存配置
- 遍历配置,让数据通过校验器,从而得到不能通过校验的数组 res
- res 长度如果是 0 则 全部通过校验,否则 表明不能通过校验
以上 1 和 2 都属于配置,而 3 和 4 有固定的模式,传递回调函数 checkAll 和 faild 来处理比较好。
- 虽然已经有配置的感觉了,但是还需要差些封装、而且功能还缺乏一些:
- method 应该是 数组,因为有些值的校验器不止一个 需要串联起来 (像这样 method: [‘exist’, ‘lengthLessThan’])
- 尚未得到较好的封装以便在其他上下文中执行