学习下如何优雅的写表单验证

作者 LoaingMore 日期 2017-05-14
学习下如何优雅的写表单验证

最近项目挺赶,博客许久没更新(借口~),周末充下电,表单是一个项目中必不可少的模块了,像注册,登录等。学习下解决下工作中关于表单验证这块的问题

DEMO

demo源码可能与博客有些略微不同(但是核心思想一模一样)
线上案例

问题

看看平时我写表单的代码风格吧(求轻喷~)

if(userName.value === ''){
console.log("姓名不为空");
return;
}
if(!reg.test(userName.value)){
console.log("姓名不合格");
return;
}
if(userPass.value === ''){
//.........
}
if(userPass.value !=== reUserPass.value){
console.log("密码不一致");
return;
}
if(mobile.value === ''){
console.log("手机号码不能为空");
return;
}
//此处略去n多if-esle

这样编写代码对于实现业务需求来说是没有问题的,但是总觉得哪里怪怪的:

  • 我有密集恐惧症,那么多的if-else看着怪恶心的
  • 验证的规则复用性太差了,哪里需要,只是拷贝来,粘贴过去,飞来飞去
  • 这样的代码容易被喷,不容易维护

怎么解决呢

假如我们不想使用那么多的if语句,那么你心中理想的验证表单的方式是什么呢?写一系列规则,最后一步验证,这样写起来很嗨吧

// 首页获取form元素
var registerForm = document.querySelector('#re-form');
// 创建实例
var validator = new Validator();
validator.add(registerForm.userName,'isEmpty','用户名不能为空');
validator.add(registerForm.userPass,'minLength:6','密码至少六位');
//校验信息
var errorMsg = validator.start();
if(errorMsg){
console.log(errorMsg);
}

如果能这样写那就太爽了,很优雅,很方便,下一步需要了解下什么是策略模式

策略模式

所谓策略就是做事情的方法,好比玩三国杀,每个人都有自己的技能,每个人物都有自己的制胜策略;比如去旅游,就有很多种路线供你选择,要么坐飞机,要么坐高铁,要么徒步;

所以,做一件事情会有很多种方法,接下来编码也会以这种方式进行;核心思想是,将要做什么和谁去做进行分离。一个完整的策略需要两个类:策略类,环境类;环境类接收请求,但不处理请求,它会把请求委托给策略类,让策略类去处理,而策略类的扩展是很容易的,这样,使得我们的代码易于扩展。
在表单验证的例子中,各种验证的方法组成了策略类,比如:判断是否为空的方法(如:isNonEmpty),判断最小长度的方法(如:minLength),判断是否为手机号的方法(isMoblie)等等,他们组成了策略类,供给环境类去委托请求。下面,我们就来实战一下。

策略类

// 完成我们的策略表 (自由定制)
var ruleList = {
//验证为空
isEmpoty: function(value,errorMsg){
if(value == ''){
return errorMsg;
}
},
//验证最小长度
minLength: function(value,len,errorMsg){
if(value.length < len){
return errorMsg;
}
},
//验证手机号
isMobile: function(value,errorMsg){
if(!/^1\d{10}$/.test(value)){
return errorMsg;
}
}
};

环境类

根据之前的畅想:

validator.add(registerForm.userName,'isEmpty','用户名不能为空');
validator.add(registerForm.userName,'isMobile','手机号码有误');

我们可能需要创建一个类,类里面有两个方法 add()add()接收三个参数,第一个是表单字段(校验对象),第二个是策略方法名字,使用冒号(:)分隔,亲一个是方法名字,后一个是传给该方法的参数(如长度),第三个是验证不通过的返回信息;
然后用start方法进行验证:

var errorMsg = validation.start();

但是这种参数配置还是有问题,我们的要求是多种校验规则,比如用户名既不能为空,又要满足用户名长度不小于6,并不是单一的,上面的为什么要写两次,这种看着就不舒服,这时候我就需要对配置参数做一点小小的改动,我们用数组来传递多个校验规则:

//最终实现
validator.add(registerForm.username, [{
strategy: 'isEmpty',
errorMsg: '用户名不能为空!'
}, {
strategy: 'minLength:6',
errorMsg: '用户名长度不能小于6位!'
}])

实现

var FormValidator = function(ruleList){
//保存策略规则列表
this.strategies = ruleList;
//储存规则方法
this.validationFns = [];
};
FormValidator.prototype = {
add:function(dom,rule){
var that = this;
for(var i=0;i<rule.length;i++){
// 这里使用闭包储存i 动态添加方法
(function(i){
//
that.validationFns.push(function(){
/*
* aryNames 规则名字(包含传的参数minLeng:6) 所以需要动态的解析
* errorMsg 错误信息
* rulename 方法名字
* dataArr 储存参数 value method名字 错误信息
* strategies 使用apply 向指定方法传参数
*/
var aryNames = rule[i].strategy.split(':'),
errorMsg = rule[i].errorMsg,
rulename = aryNames[0],
dataArr = [];
dataArr.push(dom.value);
if(aryNames[1]){
dataArr.push(aryNames[1]);
}
dataArr.push(errorMsg);
return that.strategies[rulename].apply(dom,dataArr);
})
})(i)
}
},
start:function(){
var that = this;
// 遍历规则集合 抛出错误
for(i in that.validationFns){
var msg = that.validationFns[i]();
if(msg){
return msg;
}
}
}
};

规则集合

// 规则集合
var rules = {
//验证为空
isEmpoty: function(value,errorMsg){
if(value == ''){
return errorMsg;
}
},
//验证最小长度
minLength: function(value,len,errorMsg){
if(value.length < len){
return errorMsg;
}
},
//验证手机号
isMobile: function(value,errorMsg){
if(!/^1\d{10}$/.test(value)){
return errorMsg;
}
}
};

客户端调用代码方法

<form id="submit">
<input type="text" class="username" name="username">
<input type="text" class="userpass" name="userpass">
</form>
<div class="btn">提交</div>

var forms = document.querySelector('#submit');
var btn = document.querySelector('.btn');
// 构造函数 传入规则
var validation = new FormValidator(rules);
// 直接调用规则方法即可 返回错误信息
function getErrorMsg(){
// 直接调用规则方法即可
validation.add(forms.username,[
{
strategy:'isEmpoty', //strategy 为固定字段
errorMsg:'用户名不能为空' //errorMsg 为固定字段
},
{
strategy:'minLength:6',
errorMsg:'用户名长度最低6位'
}
]);
validation.add(forms.userpass,[
{
strategy:'isMobile',
errorMsg:'手机号码有误'
}
]);
var error = validation.start();
return error;
}
// 点击事件
btn.addEventListener('click',function(){
var msg = getErrorMsg();
if(msg){
console.log(msg)//错误提示
}else{
//在此提交数据
}
},false)

在修改某个校验规则的时候,只需要编写或者改写少量的代码。比如我们想要将用户名输入框的校验规则改成用户名不能少于4个字符。可以看到,这时候的修改是毫不费力的。代码如下:

// 更具体的方法 可以在rules中修改
validation.add(forms.username, [{
strategy: 'isNonEmpty',
errorMsg: '用户名不能为空!'
}, {
strategy: 'minLength:4',
errorMsg: '用户名长度不能小于4位!'
}])

参考:
策略模式在表单中的应用