2017-05-16
JavaScript
FE
配置式表单验证 (1)
以前我写表单验证:一坨坨的 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) 用以返回布尔值来表示相反的两个含义:
  1. 返回 true 则代表通过校验
  2. 返回 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];
  1. methods 是方法集,表明必须都通过这些校验。
  2. args 是执行这些方法的时候 传给这些方法的参数。
  3. 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}

# 下篇

  1. checker 保存校验器
  2. vals 保存配置
  3. 遍历配置,让数据通过校验器,从而得到不能通过校验的数组 res
  4. res 长度如果是 0 则 全部通过校验,否则 表明不能通过校验
以上 1 和 2 都属于配置,而 3 和 4 有固定的模式,传递回调函数 checkAll 和 faild 来处理比较好。

  • 虽然已经有配置的感觉了,但是还需要差些封装、而且功能还缺乏一些:
  • method 应该是 数组,因为有些值的校验器不止一个 需要串联起来 (像这样 method: [‘exist’, ‘lengthLessThan’])
  • 尚未得到较好的封装以便在其他上下文中执行




回到顶部