Angular 输入框实现自定义验证功能

<p style="text-align: left">此插件使用angular.js、JQuery实现。(jQuery的引入需在angular 之前)

  用户可以 在输入框输入数据后验证 必填项、整数型、浮点型验证。

  如果在form 里面的输入框验证,可以点击 提交按钮后,实现 必填项验证。

效果图如下:

(1)验证未通过时,背景标红等样式为  

rush:js;"> input.ng-invalid,select.ng-invalid { background-color: #ee82ee !important; border: 1px solid #CCC; } .qtip { position: absolute; max-width: 260px; display: none; min-width: 50px; font-size: 10.5px; line-height: 12px; direction: ltr; } .qtip-content { position: relative; padding: 5px 9px; overflow: hidden; text-align: left; word-wrap: break-word; } .qtip-rounded,.qtip-tipsy { -moz-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; } .qtipmodal-ie6fix { position: absolute !important; } .Box-shadow-tips { background-color: #F63; border-color: #F5A88F; color: white; -moz-Box-shadow: 2px 2px 2px #969696; -webkit-Box-shaow: 2px 2px 2px #969696; Box-shadow: 2px 2px 2px #969696; }

  因为angular.js 内置验证未通过时,会自动标签 增加 .ng-invalid 样式,因为这里重写此样式

rush:js;">  input.ng-invalid,select.ng-invalid {   background-color: #ee82ee !important;   border: 1px solid #CCC;   } 

 (2)HTML 代码如下

rush:js;">

(3)此插件使用 directive myValid 实现

rush:js;"> app.directive('myValid',['$parse','uiTipsFactory','uiValidFactory',function ($parse,tips,valid) { var uiValidAttrIdName = 'ui-valid-id'; return { restrict: 'A',require: 'ngModel',link: function (scope,el,attrs,ctrl) { var validId = el.attr(uiValidAttrIdName); if (!validId) { validId = Math.guid(); el.attr(uiValidAttrIdName,validId); } var getRules = function () { return attrs.myValid; }; var lastOldRules; var validFn = function (value,oldRules) { var sp = '_'; var rules = getRules(); var r = valid.check(value,rules,scope,attrs.uiValidTips); if (lastOldRules && !oldRules) { oldRules = lastOldRules; } if (r.flag && oldRules) { rules = rules ? rules + ' ' + oldRules : oldRules; } if (rules) { var arrInner = rules.split(' '); var i = 0; for (; i < arrInner.length; i++) { var oneRule = arrInner[i]; if (!oneRule.trim()) { continue; } ctrl.$setValidity(attrs.ngModel + sp + oneRule,r.flag ? true : oneRule != r.rule); } } if (!r.flag) { tips.on(el,r.msg); } else { tips.off(el); } return r.flag; }; var init = function () { var rules = getRules(); if (!rules) { return; } var parsers = ctrl.$parsers; if (parsers && parsers.length > 0) { parsers.clean(); } parsers.unshift(function (value) { return validFn(value) ? value : undefined; }); }; scope.$watch(attrs.ngModel,function (newVal,oldVal) { if (newVal === oldVal) { return; } if (ctrl.$modelValue != undefined && (ctrl.$invalid || el.hasClass('ng-invalid'))) { validFn(ctrl.$modelValue); } }); scope.$watch(getRules,function (newRules,oldRules) { init(); lastOldRules = oldRules; if (ctrl.$modelValue === undefined || ctrl.$modelValue === null) { var needValid = false; el.hasClass('ng-invalid'); var isValNaN = ctrl.$viewValue !== ctrl.$viewValue; if (ctrl.$invalid || (ctrl.$viewValue !== undefined && !isValNaN)) { needValid = true; } if (needValid) { ctrl.$setViewValue(ctrl.$viewValue); } } else { if (!ctrl.$dirty && attrs.dirtyCheck) { console.log('----'); } else { validFn(ctrl.$modelValue,oldRules); } } }); } } }]);

  通过 监听 attrs.ngModel,验证规则 rules ,ctrl.$parser 来实现 输入框内容改变的响应。

  一旦使用此directive,则动态为当前输入框添加ID,以便在 验证通过后,改变输入框的验证背景信息。

(4)验证逻辑处理 uiValidFactory

rush:js;"> app.factory('uiValidFactory',tips) { return { check: function (val,$scope,defaultTips,extendParam) { if (!rules) { return { flag: true }; } var rulesArr = rules.split(' '),isBlank = val === null || val === undefined || val === '' || ('' + val === ''); //如果不是必填项 且没有输入值 则清除提示框 if ($.inArray('r',rulesArr) === -1 && isBlank) { return { flag: true } } var i = 0,len = rulesArr.length; for (; i < len; i++) { var rule = rulesArr[i]; if (!rule) { continue; } var flag = true; if ('r' === rule) { //如果是必填项,有值 返回true flag = !isBlank; } else if (rule.contains(':')) { //如果校验规则是 fn:ctrl.certCheck flag = this.checkRule(val,rule.split(/:/),extendParam); } else { //校验 规则是 int 用正则匹配 数字 邮箱 长度 var pat = this.pats[rule]; if (pat instanceof RegExp) { if (angular.isstring(val)) { flag = this.mat(val,pat); } } else if (angular.isFunction(pat)) { flag = pat(val); } else { flag = false; } } //这是干什么的呢 if (angular.isstring(flag)) { return { flag: false,msg: flag,rule: rule } } if (flag === false) { var msg = this.getMsg(rule,defaultTips) || this.getMsg('tips.valid'); console.log(msg); return { flag: false,msg: msg,rule: rule } } } return { flag: true } },checkRule: function (val,ruleArr,extendParam) { //ruleArr fn:certCheck var rule = ruleArr[0]; if (rule === 'fn') { fnName = ruleArr[1];//指定被调函数的名字 certCheck var fn = $parse(fnName)($scope); if (!fn) { return true; } return fn.call($scope,val,extendParam); } else { return true; } },checkValidForm: function (formName) { //只检查必填项 //使用属性筛选器 获得里面所有的元素 var formContext = $('form[name="{0}"],[ng-form="{0}"],[data-ng-form="{0}"]'.format(formName)),validList = formContext.find('[my-valid]');//validList 不是数组,是伪数组 if (!validList.length) { return; } var that = this,validFlags = []; validList.each(function () { var ele = $(this),val = ele.val(),ruleStr = ele.attr('my-valid'); if (!ruleStr) { return true; } if (angular.isstring(val)) { val = val.trim(); } var validRules = ruleStr.split(' '); if ($.inArray('r',validRules) != -1 && !val) { var modelValue = ele.attr('ng-model') || ele.attr('data-ng-model'); validFlags.push(modelValue); tips.on(ele,that.getMsg('r')); } } ); return validFlags; },mat: function (val,pat) { if (!pat) { return; } return pat.test(val); },getMsg: function (rule,tips) { tips = tips || ''; //可以在界面上直接写 tips if (tips && tips.contains(':')) { return tips; } var msg = this.msgs[rule]; if (msg) { var params0 = tips.contains(':') ? tips.split(/:/)[0] : ''; var params1 = ''; if (rule.startsWith('min') || rule.startsWith('max')) { var ruleArr = rule.split(/:/); params1 = ruleArr[ruleArr.length - 1]; } return msg.format(params0,params1); } else { } },regPat: function (code,pat,msg) { if (this.pat[code]) { return; } this.pats[code] = pat; this.msgs[code] = msg; },msgs: { 'r': '必填','int': '{0}必须为整数' },pats: { 'int': /^[\-\+]?([0-9]+)$/ } } } ]) ;

通过获取输入框 ele.myValid 验证规则,

   1、如果是必填,则返回 标红此输入框,鼠标移上,则显示 验证信息 “必填””。

   2、如果是整数、浮点型等验证,则通过 正则表达式进行验证。

3、如果是最大(max)、最小(min),则自定义逻辑。

  4、如果是 fn 验证,则根据 对应controller中函数进行验证。

  5、用户点击提交按钮,则 判断是否必填项,验证不通过,对应元素背景标红。

(5) 验证不通过,提示Factory---uiTipsFactory

<div class="jb51code">
<pre class="brush:js;">
app.factory('uiTipsFactory',function () {
return {
filterClass: function (ele,invalid) {
if (invalid) {
//如果验证不通过
ele.removeClass('ng-valid').removeClass('ng-pristine').addClass('ng-invalid').addClass('ng-dirty');
} else {
ele.removeClass('ng-invalid').addClass('ng-valid');
}
},on: function (ele,msg) {
var lastTip = ele.data('last-tip');
if (lastTip && lastTip === msg) {
return;
}
ele.data('last-tip',msg);
this.filterClass(ele,true);
var offset = ele.offset();
if (!offset.top && !offset.left && ele.is('hidden')) {
offset = ele.show().offset();
}
var id = ele.attr('ui-valid-id');
if (!id) {
id = Math.guid();
ele.attr('ui-valid-id',id);
}
if (id.contains('.')) {
id = id.replace(/./g,'_');
}
var top = offset.top,left = offset.left;
var getTips = function () {
var tip = $('#vtip' + id);
if (_tip.length) {
_tip.html(msg).css({
'display': 'none','top': top + 'px','left': left + ele.width() + 10 + 'px'
});
} else {
var html = '<div id="vtip_' + id + '" class="vtip qtip qtip-rounded Box-shadow-tips">' +
'<div class="qtip-content">' + msg + '

';
$(html).css({
'display': 'none','position': 'absolute','left': left + ele.width() + 10 + 'px'
}).appendTo($('body'));
}
};
var bindTipsShow = function () {
getTips();
ele.unbind('mouseenter mouseleave').bind('mouseenter',function () {
var tip = $('#vtip' + id);
if (_tip.is(':hidden')) {
tip.show();
}
}).bind('mouseleave',function () {
$('#vtip
' + id).hide();
});
};
bindTipsShow();
},off: function (ele) {
ele.data('last-tip','');
this.filterClass(ele);
var id = ele.attr('ui-valid-id');
if (!id) {
return;
}
if (id.contains('.')) {
id = id.replace(/./g,'');
}
$('#vtip
' + id).remove();
ele.unbind('mouseenter mouseleave');
}
}
});

   1、验证不通过,增加背景色,元素css处理如下

rush:js;"> ele.removeClass('ng-valid').removeClass('ng-pristine').addClass('ng-invalid').addClass('ng-dirty');

   验证通过,CSS处理如下  

rush:js;">  ele.removeClass('ng-invalid').addClass('ng-valid');

2、背景提示语,则是在body上增加一个div层。

(6)其他相关代码

32) { return "数字太大了"; } return true; }; $scope.submit = function () { if (!uiValidFactory.checkValidForm($scope.baseInfoForm.$name)) { } }; }] ); Math.guid = function () { var a = "",b = 1; for (; b <= 32; b++) { var c = Math.floor(Math.random() * 16).toString(16); a += c; if (b === 8 || b === 12 || b === 16 || b === 20) { a += '-'; } } return a; }; String.prototype.contains = String.prototype.contains || function (a) { return this.indexOf(a) != -1; }; String.prototype.format = String.prototype.format || function () { var a = Array.prototype.slice.call(arguments); return this.replace(/\{(\d+)}/g,function (c,b) { return a[b]; }) };

   整个代码如下:

rush:js;"> Meta charset="UTF-8">