我正在angularjs中构建一个验证模块,以改进一些开箱即用的基本html5验证。我的工作基于jquery验证插件。我遇到的问题是,即使一切似乎都正常工作,表单也无法正确验证。具体来说,我目前正在测试一个比标准html5版本更不慷慨的电子邮件验证,因为它需要一个包含顶级域名的完整电子邮件,但是当我调用$setValidity('email',false);
时,表单仍然有效。我的代码在
验证服务
'use strict';
/* Services */
function validations() {
/**
* Created by rich on 8/14/15.
*
* Most of these regex statements were kifed from jquery validation plugin with perhaps a little bit of sugar
* added to some by yours truly.
*
* Thanks to jzaeffer for his efforts.
* http://jqueryvalidation.org
* https://github.com/jzaefferer/jquery-validation
*/
var tests = {
//validates full email address with a valid domain. [string]@[string].[string]
email: function(value, param, elem) {
return /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])+\.+(?:[a-zA-Z0-9\.](?:[a-zA-Z0-9-\.]{0,61}[a-zA-Z0-9])?)$/.test(value);
},
url: function(value, param, elem) {
return /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(value)
},
integer: function(value, param, elem) {
return /^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(value);
},
creditcard: function(value, param, elem) {
// accept only spaces, digits and dashes
if (/[^0-9 \-]+/.test(value)) {
return false;
}
var nCheck = 0,
nDigit = 0,
bEven = false,
n, cDigit;
value = value.replace(/\D/g, "");
// Basing min and max length on
// http://developer.ean.com/general_info/Valid_Credit_Card_Types
if (value.length < 13 || value.length > 19) {
return false;
}
for (n = value.length - 1; n >= 0; n--) {
cDigit = value.charAt(n);
nDigit = parseInt(cDigit, 10);
if (bEven) {
if ((nDigit *= 2) > 9) {
nDigit -= 9;
}
}
nCheck += nDigit;
bEven = !bEven;
}
return (nCheck % 10) === 0;
},
min: function(value, param, elem) {
return value >= param
},
max: function(value, param, elem) {
return value <= param
},
between: function(value, param, elem) {
return (value >= param[0] && value <= param[1]);
},
equalTo: function(value, param, elem) {
return value === elem.val();
},
//Accept specific mime type
accept: function(value, param, elem) {
// Split mime on commas in case we have multiple types we can accept
var typeParam = typeof param === "string" ? param.replace(/\s/g, "").replace(/,/g, "|") : "image/*",
i, file;
// If we are using a wildcard, make it regex friendly
typeParam = typeParam.replace(/\*/g, ".*");
// Check if the element has a FileList before checking each file
if (elem.files && elem.files.length) {
for (i = 0; i < elem.files.length; i++) {
file = elem.files[i];
// Grab the mimetype from the loaded file, verify it matches
if (!file.type.match(new RegExp("\\.?(" + typeParam + ")$", "i"))) {
return false;
}
}
}
// Either return true because we've validated each file, or because the
// browser does not support element.files and the FileList feature
return true;
},
alphanumeric: function(value, param, elem) {
/^\w+$/i.test(value);
},
ipv4: function(value, param, elem) {
return /^(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)$/i.test(value);
},
ipv6: function(value, param, elem) {
return /^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i.test(value)
},
phone: function(value, param, elem) {
return (value.length > 9 && value.match(/^(\+?1-?)?(\([2-9]([02-9]\d|1[02-9])\)|[2-9]([02-9]\d|1[02-9]))-?[2-9]([02-9]\d|1[02-9])-?\d{4}$/));
},
zip: function(value, param, elem) {
return /^\d{5}(-\d{4})?$/.test(value)
}
};
function addTest(name, test) {
tests[name] = test;
}
function isValid(test, value, params, elem) {
var valid = tests[test](value, params, elem);
return valid
}
return {
addTest: function(name, test) {
addTest(name, test);
},
test: function(testName, options) {
if (typeof options === 'object') {
return isValid(testName, options.value, options.param, options.elem);
} else {
//options is actually just the value to be tested
return isValid(testName, options);
}
},
validate: function(name, ngModel) {
var validator;
var self = this;
if (self.isType(name)) {
validator = function(ngModelValue) {
//Custom Property used for debugging
ngModel.$setValiditityTo = this.test(name, ngModelValue);
//Set validity based on results of test
ngModel.$setValidity(name, this.test(name, ngModelValue));
return ngModelValue
};
//Bind validator to current this object so that it can run the required tests.
ngModel.$parsers.push(validator.bind(self));
}
},
isType: function(name) {
var types = ['email', 'url', 'creditcard', 'integer', 'alphanumeric', 'ipv4', 'ipv6', 'phone', 'zip'];
return types.indexOf(name) > -1;
}
}
}
app.factory('validations', validations);
输入指令
function input(validations) {
return {
restrict: 'E',
require: 'ngModel',
scope: {
type: '@'
},
link: function(scope, elem, attr, ctrl) {
validations.validate(scope.type, ctrl);
}
};
}
app.directive('input', input);
在下面的plunker中,您可以看到自定义$setValidityTo
对象设置正确,但errors对象遵循电子邮件验证的正常行为。
答案 0 :(得分:1)
问题是当字段无效时验证函数返回的内容:
PreviewCallback
以上总是返回模型值。
回想一下,$ parsers / $ formatters是一个“管道”。验证函数按添加顺序执行,如果其中一个验证函数认为该值无效,则返回undefined。
当验证函数返回undefined时,管道中剩余的验证器函数不会被执行。
Working plunkr,以及更改过的代码:
validator = function(ngModelValue) {
//Custom Property used for debugging
ngModel.$setValiditityTo = this.test(name, ngModelValue);
//Set validity based on results of test
ngModel.$setValidity(name, this.test(name, ngModelValue));
return ngModelValue
}