最近给项目全局添加了表单验证。
一,项目需求:
需要项目全局添加输入验证,涉及到对所有的输入框友好提示非法字符,验证URL,验证电话号码,插入emoji提示,ip格式,email ,唯一性等十几条验证。
因为涉及众多模块,所以希望业务开发人员可以直接配置验证规则,简便开发模式。
场景,用户输入信息,焦点失去,依次验证所需验证的信息,提示未符合要求的验证信息
本身项目是含有对输入验证的组件,但未满足UI和整体当前对验证的需求
二,表单验证插件思路:
验证插件,业务模块逻辑中,配置需要验证的input/textarea ,及规则项,可自定义提示消息;
验证分为三类,(1)类似ip,email,url,正则类验证;(2)唯一性 需要与后端交互验证 (3)长度,值大小模块内需自定义规则
大致思路封装各种验证方法,根据暴露出的配置方法及属性,选择验证方法,验证当前ele是否通过验证,未通过验证,页面提示信息;
调用类似:
var config = {
el: $(".validatorCon", $el), //要验证的表单或表单容器
submitBtn: $("#userserach001", $el), //触发验证的按钮,可不配置
dialog:true, //是否弹出验证结果对话框
rules:{
tenantPhone:'num1|max-11', //模糊查询 最小值为三位;
tenantName:'definitionID|max-20'
}
}
formValidator = new Compts.Validator(config);
三,具体实现:
(1)验证正则
var SnRegExp = {
email: "^[\\w!#$%&'*+/=?^_`{|}~-]+(?:\\.[\\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\\w](?:[\\w-]*[\\w])?\\.)+[\\w](?:[\\w-]*[\\w])?", //邮件
idcard: "^(^\\d{15}$|^\\d{18}$|^\\d{17}(\\d|X|x))$", //身份证
letter: "^[A-Za-z]+$", //字母
codeID:"^[_a-zA-Z0-9]+$", //服务、能力编码,只含有数字、字母、下划线
ip4Fuz:"^((25[0-5]|2[0-4]\\d|[0-1]\\d{2}|[1-9]?\\d)\\.){0,3}(25[0-5]|2[0-4]\\d|[0-1]\\d{2}|[1-9]?\\d){0,1}$"//模糊查询ip
//省略.....
};
(2)验证方法
*常规正则验证
"moneymin":function(str, _min){
return (str >= parseInt(_min));
},
"ip4":function(str){
return new RegExp(SnRegExp.ip4).test(str);
},
//判断是否符合IP
"ip4Fuz":function(str){
return new RegExp(SnRegExp.ip4Fuz).test(str);
},
//判断是否符合金钱,不能为负数,保留两位小数
"money":function(str){
return new RegExp(SnRegExp.money).test(str);
},
*唯一性验证
"Uniqueness":function(str,url){
var returnVal=true;
var urlUn = encodeURI(url+str);
Util.ajax.postJson(urlUn,null,function(json,status){
if(status){
console.log("通过唯一性验证");
}else{
//唯一性返回标志,返回-8888
if(json.returnCode=="-8888")
{
returnVal=false;
}else if(json.returnCode=="-9999"){
Util.dialog.tips("数据库验证失败,请稍后重试");
returnVal=false;
msgTips=false;
}else{
returnVal=true;
}
}
},true)
return returnVal;
}
*json /长度等验证
// 判断是否为json格式
var isJson =function (str){
if(str.substr(0, 1)=="{"&&str.substr(str.length-1,1)=="}"){
try {
JSON.parse(str)
return true;
} catch (err) {
return false;
}
}else{
return false;
}
}
(3)插件返回方法对象
(4)事件绑定
eventInit: function () {
this.getItems();
//提交按钮点击事件
if (this.submitBtn && this.submitBtn.length) {
this.submitBtn.on("click", $.proxy(function (e) {
if (this.dealValidateArr()) {
this.returnObj.trigger("success");
}
}, this));
}
//重置按钮点击事件
if (this.resetBtn && this.resetBtn.length) {
this.resetBtn.on("click", $.proxy(function (e) {
verifyOnOff = true;
this.form[0].reset();
this.returnObj.trigger('reset', e);
}, this));
}
this.form.on("click", $.proxy(function () {
// this.getItems();
}, this));
this.form.on('scroll', $.proxy(function () {
}, this))
},
(5)循环得到每一验证
getItems: function (callback) {
if (this.items) {
for (var item in this.rules) {
if (!this.items[item]) {
var ele = this.form.find("[name=" + item + "]");
if (ele.length) {
this.items[item] = {
name: item,
el: ele,
tagName: ele[0].tagName,
rules: this.rules[item].split("|")
};
this.eventEleInit(this.items[item]);
}
}
if (this.items[item] && callback) {
var $ele = this.form.find("[name=" + item + "]");
$.each($ele, $.proxy(function (k, v) {
callback.call(this, this.items[item], v);
}, this))
}
}
}
},
(6)验证是否通过
verify: function (item, ele) {
var errorText = '';
$.each(item.rules, $.proxy(function (kev, v) {
var validate = this.getRule(v);
if (validate.is_rule) {
var value = $(ele).val();
if (($.inArray("required", item.rules) != -1) || (this.validateRules["required"](value))) {
if (validate.validate(value)) {
if (this.messages && this.messages[item.name] && this.messages[item.name][validate.val]) {
errorText = this.messages[item.name][validate.val]
} else {
if(! this.validateRules["xssReg"](value)){
var t =new RegExp(SnRegExp.xssReg);
errorText=defaultMsg.xssReg+t.exec(value)[0];
}else{
errorText = this.defaultMsgFun(validate);
}
}
return false
}
}
}
}, this));
return errorText;
},
(7)判断value是否通过验证 返回错误信息
defaultMsgFun: function (obj) {
var error = "验证不通过";
if (this.defaultMsg[obj.val]) {
error = this.defaultMsg[obj.val].replace('\{\{0\}\}', obj.str1 ? obj.str1 : "").replace('\{\{1\}\}', obj.str2 ? obj.str2 : "")
}
return error;
},
(8)UI提示信息
errorFun: function (errorText, ele, is_tips) {
var _self = this;
if (errorText) {
// item.el.parent().addClass("validate-error");
var posEle;
if ($(ele).css('display') == 'none') {
posEle = $(ele).siblings()[0];
} else {
posEle = ele;
}
$(posEle).addClass("validate-error");
if (is_tips&&msgTips) {
// 做一个dialog
clearTimeout(ele.timer);
var t = $(posEle).offset().top,
l = $(posEle).offset().left,
T = this.form.offset().top,
L = this.form.offset().left,
scrollT = this.form[0].scrollTop,
scrollL = this.form[0].scrollLeft,
eleH = posEle.offsetHeight,
eleW = posEle.offsetWidth,
textHeight = 23;
var $nowSpan=$('<div class="errorText"></span><span class="icon-i">!</span><span class="error-text"></span></div>');
// var $nowSpan = $('<span class="errorText"></span>');
if (_self.msgPos === 'right') {
$nowSpan.addClass("arrow-left").data('elem', ele).appendTo(this.form).css({ top: t - T + scrollT+(eleH-textHeight)/2, left: l - L + scrollL + eleW + 3 ,width:eleW});
} else if (_self.msgPos === 'bottom'){
$nowSpan.addClass("arrow-bottom").data('elem', ele).appendTo(this.form).css({ top: t - T + scrollT + eleH, left: l - L + scrollL ,width:eleW});
} else { // top
$nowSpan.addClass("arrow-top").data('elem', ele).appendTo(this.form).css({ top: t - T + scrollT - textHeight, left: l - L + scrollL ,width:eleW});
}
$nowSpan.find('.error-text').text(errorText);
ele.timer = setTimeout(function () {
$nowSpan.remove();
verifyOnOff = false;
$(posEle).removeClass("validate-error");
}, 2500)
}
} else {
$(posEle).removeClass("validate-error");
}
msgTips=true;
}
// dialogClose: function () {
// dialog.get("sn-validate") && dialog.get("sn-validate").close().remove();
// }
});