2013年9月24日星期二

jQuery.validationEngine.js learning

 

used in the project to this plug-in, draw a blank look.

 
  
(function($){ 
var method ={}
$.fn.validationEngine
= function(){}
$.validationEngine
= {}
$(
function(){$.validationEngine.defaults.promptPosition = methods.isRTL()?'topLeft':"topRight"});
})(jQuery)
 
 

look at the structure , it is quite clear . jQuery's dom object directly call on the line, I get the following on the official jQuery an example to illustrate

 

look at an example :

 
  
<!DOCTYPE html> 
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<title>JQuery Validation Engine</title>
<link rel="stylesheet" href="../css/validationEngine.jquery.css" type="text/css"/>
<link rel="stylesheet" href="../css/template.css" type="text/css"/>
<script src="../js/jquery-1.8.2.min.js" type="text/javascript">
</script>
<script src="../js/languages/jquery.validationEngine-en.js" type="text/javascript" charset="utf-8">
</script>
<script src="../js/jquery.validationEngine.js" type="text/javascript" charset="utf-8">
</script>
<script>
jQuery(document).ready(
function(){
// binds form submission and fields to the validation engine
jQuery("#formID").validationEngine();//初始化
});

</script>
</head>
<body>
<p>
This demonstration shows the prompt position adjustments
<br/>
</p>
<div id="test" class="test" style="width:150px;">This is a div element</div>
<form id="formID" class="formular" method="post" action="">
<fieldset>
<label>
<span>Fields are required : </span>
<input value="" class="validate[required] text-input" type="text" name="req" id="req" data-prompt-position="topRight:-70" />
</label>
<label>
<input value="" class="validate[required] text-input" type="text" name="req2" id="req2" data-prompt-position="topRight:-30" />
</label>
<label>
<span>Additional info:</span>
<textarea class="validate[required]" id="add" style="width:250px;height:50px;" data-prompt-position="bottomRight:-70,3"></textarea>
</label>
<br/>
<br/>
</fieldset>
<input class="submit" type="submit" value="Validate &amp; Send the form!"/><hr/>
</form>
</body>
</html>
 
 

can see the page directly call the validationEngine method .

 
  
 $.fn.validationEngine = function(method) { 
var form = $(this);//获取form对象
if(!form[0]) return form; // stop here if the form does not exist 如果不是jQuery的dom对象则返回

if (typeof(method) == 'string' && method.charAt(0) != '_' && methods[method]) {

// make sure init is called once
if(method != "showPrompt" && method != "hide" && method != "hideAll")
methods.init.apply(form);
return methods[method].apply(form, Array.prototype.slice.call(arguments, 1));//支持传入原型上的方法执行该方法
} else if (typeof method == 'object' || !method) {
//该例子中method为undefined
// default constructor with or without arguments
methods.init.apply(form, arguments);//调用方法对象中的init方法
return methods.attach.apply(form);//调用方法对象中的attach方法
} else {
$.error(
'Method ' + method + ' does not exist in jQuery.validationEngine');
}
}
 
 

Here , you can see if you want the validation controls do not work , the method in validationEngine passed three kinds of parameters , showPrompt, hide, hideAll. So will later analysis , you will see , as long as the code is called at three parameters passed to this plug-in will not work , because the methods.init, attach method has not been executed , the result of initialization . In fact, if you pass like '1 ' such parameters , plug-ins will not work , but it will complain , into the final judgment call $. Error thrown.

 

The following is the workflow engine

 

 

using jquery.validationEngine.js also need a js file : includes many language packs , where we used to be a model en package introduced jQuery.validationEngine-en.js

 
  
(function($){ 
$.fn.validationEngineLanguage
= function(){
};
$.validationEngineLanguage
= {
newLang:
function(){
$.validationEngineLanguage.allRules
={}
}
};

$.validationEngineLanguage.newLang();

})(jQuery);
 
 

validationEngineLanguage method directly added to the jquery prototype , where the default is not initialized anything and the actual development, according to the demand to make the appropriate changes, the default code execution again $. validationEngineLanguage.newLang (), let $. validationEngineLanguage have allRules property carefully look at this allRules content

 
  
"required": { // Add your regex rules here, you can take telephone as an example 
"regex": "none",
"alertText": "* This field is required",
"alertTextCheckboxMultiple": "* Please select an option",
"alertTextCheckboxe": "* This checkbox is required",
"alertTextDateRange": "* Both date range fields are required"
}

"phone": {
// credit: jquery.h5validate.js / orefalo
"regex": /^([\+][0-9]{1,3}[\ \.\-])?([\(]{1}[0-9]{2,6}[\)])?([0-9\ \.\-\/]{3,20})((x|ext|extension)[\ ]?[0-9]{1,4})?$/,
"alertText": "* Invalid phone number"
}
....
 
 

similar page write a required, this string is associated with a lot of information, including an empty pop-up information , phone the string is associated with a matching regular and pop-up error message. If you need to add a new match function can be added here .

 

Method.init

 
  
init: function(options) { 
var form = this;
if (!form.data('jqv') || form.data('jqv') == null ) {
options
= methods._saveOptions(form, options);
// bind all formError elements to close on click
$(document).on("click", ".formError", function() {//让html标签去监听拥有formError类的元素绑定click事件
$(this).fadeOut(150, function() {//实现淡出效果
// remove prompt once invisible
$(this).parent('.formErrorOuter').remove();//删除其父节点
$(this).remove();//删除本身节点
});
});
}
return this;
}
 
 

here . formError is a pop-up error message class here in the init method to these pop-up message add click event for close them.

 

Method._saveOptions

 
  
_saveOptions: function(form, options) { 
// is there a language localisation ?
if ($.validationEngineLanguage)
var allRules = $.validationEngineLanguage.allRules;
else
//这里的else语句就是else下面的紧跟它的第一句代码,不推荐这样写。
$.error("jQuery.validationEngine rules are not loaded, plz add localization files to the page");
// --- Internals DO NOT TOUCH or OVERLOAD ---
// validation rules and i18
$.validationEngine.defaults.allrules = allRules;
var userOptions = $.extend(true,{},$.validationEngine.defaults,options);//保存错误规则
form.data('jqv', userOptions);//将userOptions信息保存在jqv属性中
return userOptions;
}
 
 

mainly import jQuery.validationEngine-en.js content , save the information to jqv attributes , and returns this information.

 

Method.attach

 
  
attach: function(userOptions) {//调用者是被验证的form 

var form = this;
var options;
if(userOptions)
options
= methods._saveOptions(form, userOptions);
else
options
= form.data('jqv');//读取form的jqv信息
//寻找data-validation-engine属性中拥有validate的标签
options.validateAttribute = (form.find("[data-validation-engine*=validate]").length) ? "data-validation-engine" : "class";
if (options.binded) {
//绑定blur事件,如果没有data-validation-engine属性的,那必须拥有包含validate字符串的类,且不是checkbox,radio或者是类datepicker(时间控件)的元素将拥有blur事件
//为checkbox,radio必须要有validate的控件绑定click事件
//单独为时间控件绑定blur时间,传入{'delay':300}
// delegate fields
form.on(options.validationEventTrigger, "["+options.validateAttribute+"*=validate]:not([type=checkbox]):not([type=radio]):not(.datepicker)", methods._onFieldEvent);
form.on(
"click", "["+options.validateAttribute+"*=validate][type=checkbox],["+options.validateAttribute+"*=validate][type=radio]", methods._onFieldEvent);
form.on(options.validationEventTrigger,
"["+options.validateAttribute+"*=validate][class*=datepicker]", {"delay": 300}, methods._onFieldEvent);
}
if (options.autoPositionUpdate) {
$(window).bind(
"resize", {
"noAnimation": true,
"formElem": form
}, methods.updatePromptsPosition);
}
//为拥有data-validation-engine-skip属性的a标签和button,input标签,类拥有validate-skip字段的a标签和button,input标签绑定click事件。
form.on("click","a[data-validation-engine-skip], a[class*='validate-skip'], button[data-validation-engine-skip], button[class*='validate-skip'], input[data-validation-engine-skip], input[class*='validate-skip']", methods._submitButtonClick);
form.removeData(
'jqv_submitButton');//删除jqv_submitButton信息

// bind form.submit
form.on("submit", methods._onSubmitEvent);//绑定submit
return this;
}
 
 

basically controls bound to trigger events , here also mention that in our submission form , it will trigger the plug-in content submit an event listener , in fact, before you commit plugin will help you check again over controls. If all the conditions are met , then will be submitted . Basically you can see a lot of the controls are bound to trigger an event method._onFieldEvent method

 

 

Here we take a look _onFieldEvent .

 
  
_onFieldEvent: function(event) { 
var field = $(this);
var form = field.closest('form, .validationEngineContainer');//找到form对象
var options = form.data('jqv');//读取jqv属性信息
options.eventTrigger = "field";
// validate the current field
window.setTimeout(function() {
methods._validateField(field, options);
if (options.InvalidFields.length == 0 && options.onFieldSuccess) {
options.onFieldSuccess();
}
else if (options.InvalidFields.length > 0 && options.onFieldFailure) {
options.onFieldFailure();
}
}, (event.data)
? event.data.delay : 0);

}
 
 

into setTimeout function will be executed , the datepicker plugin here considered a problem .

 

here _validateField all the code is not posted , analyze a few important parts inside

 
  
var rulesParsing = field.attr(options.validateAttribute);//获取控件的类或者data-validation-engine属性 
var getRules = /validate\[(.*)\]/.exec(rulesParsing);//var getRules = rulesParsing.match(/validate\[(.*)\]/);数组中第一个是匹配整条正则,第二个成员是匹配子表达式
if (!getRules)
return false;//如果数组为空,表示不匹配,则返回
var str = getRules[1];//获取子表达式匹配的内容
var rules = str.split(/\[|,|\]/);//将以类似[,],','分开字符串,考虑validate[]中嵌套多个内容


for (var i = 0; i < rules.length; i++) {
rules[i]
= rules[i].replace(" ", "");//去掉空白
// Remove any parsing errors
if (rules[i] === '') {
delete rules[i];//删除空的规则
}
}
 
 

Here is parsing rules , modify the original example

 
  
<input value="" class="validate[required,custom[email]] text-input" type="text" name="req" id="req" data-prompt-position="topRight:-70" />
 
 

here why not write directly validate [required, email], or validate [required, xx [email]] (xx not custom) a ? With these questions , we look at source code.

 

source code getRules using exec to get results that match the text , where the match result [validate [reuqired, custom [email]] text-input, required, custom [email]]. Look str, its value is required, custom [email], and then through the split -separated , rule becomes [required, custom, email,''], and finally through the for loop will be an empty string member delete away, The end result is [required, custom, email, undefined]. As for why to add this custom, do not worry , we slowly see .

 In fact, this plugin will control

need to perform validation rules are written in a class class, that we validate [] to write some rules, then control will enforce these rules in fact required is expressed as required , email it means to input email format. Both must be met.

 

code below will go through a for loop , but note here one more switch judgment Some of the rules rules eleven filtered out , perform the appropriate method. If you carefully look at this switch, you will find that there is no choice of type of email , so that you write directly on the page validate [required, email] there will be no effect . We can see that there are custom judgments. Enter a look at this custom, in the end is what

 
  
case "custom": 
errorMsg
= methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._custom);
break;
 
 

_getErrorMessage this approach plainly, is to get the error message , perform the corresponding incoming call back function

 
  
_getErrorMessage:function (form, field, rule, rules, i, options, originalValidationMethod) { 
// If we are using the custon validation type, build the index for the rule.
// Otherwise if we are doing a function call, make the call and return the object
// that is passed back.
var rule_index = jQuery.inArray(rule, rules);//确定rule在rules中的位置
if (rule === "custom" || rule === "funcCall") {
var custom_validation_type = rules[rule_index + 1];
rule
= rule + "[" + custom_validation_type + "]";
// Delete the rule from the rules array so that it doesn't try to call the
// same rule over again
delete(rules[rule_index]);
}
// Change the rule to the composite rule, if it was different from the original
var alteredRule = rule;


var element_classes = (field.attr("data-validation-engine")) ? field.attr("data-validation-engine") : field.attr("class");
var element_classes_array = element_classes.split(" ");

// Call the original validation method. If we are dealing with dates or checkboxes, also pass the form
var errorMsg;
if (rule == "future" || rule == "past" || rule == "maxCheckbox" || rule == "minCheckbox") {
errorMsg
= originalValidationMethod(form, field, rules, i, options);
}
else {
errorMsg
= originalValidationMethod(field, rules, i, options);//执行回调函数,获得错误信息
}

// If the original validation method returned an error and we have a custom error message,
// return the custom message instead. Otherwise return the original error message.
if (errorMsg != undefined) {
var custom_message = methods._getCustomErrorMessage($(field), element_classes_array, alteredRule, options);
if (custom_message) errorMsg = custom_message;
}
return errorMsg;

}
 
 

Here is method._custom method

 
  
_custom: function(field, rules, i, options) { 
var customRule = rules[i + 1];
var rule = options.allrules[customRule];
var fn;
if(!rule) { //规则没有,则提示返回
alert("jqv:custom rule not found - "+customRule);
return;
}

if(rule["regex"]) {
var ex=rule.regex;//获取相应正则
if(!ex) {
alert(
"jqv:custom regex not found - "+customRule);
return;
}
var pattern = new RegExp(ex);//转成正则表达式

if (!pattern.test(field.val())) return options.allrules[customRule].alertText;//匹配正则

}
else if(rule["func"]) { //自定义方法验证,根据返回结果判断
fn = rule["func"];

if (typeof(fn) !== "function") {
alert(
"jqv:custom parameter 'function' is no function - "+customRule);
return;
}

if (!fn(field, rules, i, options))
return options.allrules[customRule].alertText;
}
else {
alert(
"jqv:custom type not allowed "+customRule);
return;
}
}
 
 

actually see here, we generally understand the core is still called rule ['regex'] regular expression . In fact, for a mind to think about , if we write directly on the switch in the email , it would actually be dozens , or even hundreds of cases , it is evident with the write switch is unwise , this custom is equivalent to doing a simple lead role , more detailed verification of these judgments, all hang custom name , and then verified. Well, this is actually very similar _required

 
  
_required: function(field, rules, i, options, condRequired) { 
switch (field.prop("type")) {//取触发控件的类型,注意这里attr,css都不能完全取到,这里作者使用它的是prop
case "text":
case "password":
case "textarea":
case "file":
case "select-one":
case "select-multiple":
default:
var field_val = $.trim( field.val() );//去除输入的空格符
var dv_placeholder = $.trim( field.attr("data-validation-placeholder") );
var placeholder = $.trim( field.attr("placeholder") );
if (
(
!field_val )
|| ( dv_placeholder && field_val == dv_placeholder )
|| ( placeholder && field_val == placeholder )
) {
return options.allrules[rules[i]].alertText;//返回不填的错误信息
}
break;
case "radio":
case "checkbox":
// new validation style to only check dependent field
if (condRequired) {
if (!field.attr('checked')) {
return options.allrules[rules[i]].alertTextCheckboxMultiple;
}
break;
}
// old validation style
var form = field.closest("form, .validationEngineContainer");
var name = field.attr("name");
if (form.find("input[name='" + name + "']:checked").size() == 0) {
if (form.find("input[name='" + name + "']:visible").size() == 1)
return options.allrules[rules[i]].alertTextCheckboxe;
else
return options.allrules[rules[i]].alertTextCheckboxMultiple;
}
break;
}
}
 
 

been to validate these methods , if the condition is not satisfied , it will get an error message

 

end of the flowchart , the whole plug _showPrompt and _buildPrompt method is to generate the page prompts

 
  
_showPrompt: function(field, promptText, type, ajaxed, options, ajaxform) { 
var prompt = methods._getPrompt(field);
// The ajax submit errors are not see has an error in the form,
// When the form errors are returned, the engine see 2 bubbles, but those are ebing closed by the engine at the same time
// Because no error was found befor submitting
if(ajaxform) prompt = false;
// Check that there is indded text
if($.trim(promptText)){
if (prompt)
methods._updatePrompt(field, prompt, promptText, type, ajaxed, options);
else
methods._buildPrompt(field, promptText, type, ajaxed, options);
}
}
 
 

look _buildPrompt method

 
  
_buildPrompt: function(field, promptText, type, ajaxed, options) { 

// create the prompt
var prompt = $('<div>');
prompt.addClass(methods._getClassName(field.attr(
"id")) + "formError");
// add a class name to identify the parent form of the prompt
prompt.addClass("parentForm"+methods._getClassName(field.closest('form, .validationEngineContainer').attr("id")));
prompt.addClass(
"formError");//制作错误提示框,放上相应的class,这些class之前被监听过事件
switch (type) {
case "pass":
prompt.addClass(
"greenPopup");
break;
case "load":
prompt.addClass(
"blackPopup");
break;
default:
/* it has error */
//alert("unknown popup type:"+type);
}
if (ajaxed)
prompt.addClass(
"ajaxed");
// create the prompt content
var promptContent = $('<div>').addClass("formErrorContent").html(promptText).appendTo(prompt);//将错误信息放在div中插入prompt中

// determine position type
var positionType=field.data("promptPosition") || options.promptPosition;
// create the css arrow pointing at the field
// note that there is no triangle on max-checkbox and radio
if (options.showArrow) {
var arrow = $('<div>').addClass("formErrorArrow");
//prompt positioning adjustment support. Usage: positionType:Xshift,Yshift (for ex.: bottomLeft:+20 or bottomLeft:-20,+10)
if (typeof(positionType)=='string')
{
var pos=positionType.indexOf(":");
if(pos!=-1)
positionType
=positionType.substring(0,pos);
}

switch (positionType) {//生成小三角
case "bottomLeft":
case "bottomRight":
prompt.find(
".formErrorContent").before(arrow);
arrow.addClass(
"formErrorArrowBottom").html('<div class="line1"><!-- --></div><div class="line2"><!-- --></div><div class="line3"><!-- --></div><div class="line4"><!-- --></div><div class="line5"><!-- --></div><div class="line6"><!-- --></div><div class="line7"><!-- --></div><div class="line8"><!-- --></div><div class="line9"><!-- --></div><div class="line10"><!-- --></div>');
break;
case "topLeft":
case "topRight":
arrow.html(
'<div class="line10"><!-- --></div><div class="line9"><!-- --></div><div class="line8"><!-- --></div><div class="line7"><!-- --></div><div class="line6"><!-- --></div><div class="line5"><!-- --></div><div class="line4"><!-- --></div><div class="line3"><!-- --></div><div class="line2"><!-- --></div><div class="line1"><!-- --></div>');
prompt.append(arrow);
break;
}
}
// Add custom prompt class
if (options.addPromptClass)
prompt.addClass(options.addPromptClass);

// Add custom prompt class defined in element
var requiredOverride = field.attr('data-required-class');
if(requiredOverride !== undefined) {
prompt.addClass(requiredOverride);
}
else {
if(options.prettySelect) {
if($('#' + field.attr('id')).next().is('select')) {
var prettyOverrideClass = $('#' + field.attr('id').substr(options.usePrefix.length).substring(options.useSuffix.length)).attr('data-required-class');
if(prettyOverrideClass !== undefined) {
prompt.addClass(prettyOverrideClass);
}
}
}
}
prompt.css({
"opacity": 0
});
if(positionType === 'inline') {
prompt.addClass(
"inline");
if(typeof field.attr('data-prompt-target') !== 'undefined' && $('#'+field.attr('data-prompt-target')).length > 0) {
prompt.appendTo($(
'#'+field.attr('data-prompt-target')));
}
else {
field.after(prompt);
}
}
else {
field.before(prompt);
//在触发控件的前面插入
}

var pos = methods._calculatePosition(field, prompt, options);
prompt.css({
'position': positionType === 'inline' ? 'relative' : 'absolute',
"top": pos.callerTopPosition,
"left": pos.callerleftPosition,
"marginTop": pos.marginTopSize,
"opacity": 0
}).data(
"callerField", field);


if (options.autoHidePrompt) {
setTimeout(
function(){
prompt.animate({
"opacity": 0
},
function(){
prompt.closest(
'.formErrorOuter').remove();
prompt.remove();
});
}, options.autoHideDelay);
}
return prompt.animate({
"opacity": 0.87 //动画效果
});
}
 
 

generated small triangle implementations rather special . . The error content into a div , will generate div inserted in front of the trigger control , and for the div plus class, why the increase in the init method , we have to add the click event of such class , the function can be deleted by clicking them.

 

over again to complete a simple verification process.

 

In addition , the plugin also has a powerful feature that provides ajax validation.

 
  
<input type="text" name="id" id="id" class="validate[required,ajax[ajaxUserCallPhp]]"/>
 
 

These are the pages you need to add something, because associated ajaxUserCallPhp, we modify jQuery.validationEngine-en.js, find ajaxUserCallPhp

 
  
"ajaxUserCallPhp": { 
"url": "../jsp/login_check.jsp",//修改成你要跳转的路径,或者你自己新写一个关联对象
// you may want to pass extra data on the ajax call
"extraData": "name=eric",
// if you provide an "alertTextOk", it will show as a green prompt when the field validates
"alertTextOk": "* This username is available",
"alertText": "* This user is already taken",
"alertTextLoad": "* Validating, please wait"
}
 
 

this source is php, I am here temporarily to modify the jsp page .

 
  
String yc = (String)request.getParameter("fieldValue"); 
String elementId
= (String)request.getParameter("fieldId");
 
 

Note ajax request in the background how to get the value of where you use a tool like Ethereal can clearly see the url like stitching on a fieldValue = xx & fieldId = xx, so the background using the parameters passed such a catch , query the database to determine whether a user name , and finally returned to the ajax information

 
  
String json = "{\"0\":\""+elementId+"\",\"1\":\""+da+"\",\"2\":\"yc\"}"; 
out.println(json);
 
 

Here, I simply use concatenation way to pass the json formatted data to time, we make changes according to the actual situation .

 

try to find method in the _ajax method, you can find this _ajax method actually calls the $. ajax method to submit a request , notice prior to its submission , after twice for loop , mainly decompose extraData, will information assembled into json format incoming data , and finally into the request , in accordance with the data sent to the background url . About $. Ajax, there will be a success callback

 
  
success: function(json) { 
// asynchronously called on success, data is the json answer from the server
var errorFieldId = json[0];
//var errorField = $($("#" + errorFieldId)[0]);
var errorField = $("#"+ errorFieldId).eq(0);
// make sure we found the element
if (errorField.length == 1) {
var status = json[1];
// read the optional msg from the server
var msg = json[2];
if (!status) {
// Houston we got a problem - display an red prompt
options.ajaxValidCache[errorFieldId] = false;
options.isError
= true;

// resolve the msg prompt
if(msg) {
if (options.allrules[msg]) {
var txt = options.allrules[msg].alertText;
if (txt) {
msg
= txt;
}
}
}
else
msg
= rule.alertText;
if (options.showPrompts) methods._showPrompt(errorField, msg, "", true, options);
}
else {
options.ajaxValidCache[errorFieldId]
= true;

// resolves the msg prompt
if(msg) { //验证之后需要调用的参数方法,这个参数存在与allrules中if (options.allrules[msg]) {
var txt = options.allrules[msg].alertTextOk;
if (txt) {
msg
= txt;
}
}
}
else
msg
= rule.alertTextOk;
if (options.showPrompts) {
// see if we should display a green prompt
if (msg)
methods._showPrompt(errorField, msg,
"pass", true, options);
else
methods._closePrompt(errorField);
}

// If a submit form triggered this, we want to re-submit the form
if (options.eventTrigger == "submit")
field.closest(
"form").submit();
}
}
 
 

here callback function used json [0], json [1], json [2] and other ways to take the background information, personal feel not very good, this has led to many cases, the data needs to be sent back according to the API interface to encapsulate data , sometimes a bit of trouble. Custom callback feel a little better . Here I explain in the next several parameters of this callback meaning errorFieldId: ajax validation of trigger control id, and this time in the run request transfer to the background , and again returned foreground from the background , this value needs to have . status: for example , if you enter a user name , if the user name is not registered as foreground transfer a status value to non-empty , so do not tell the front desk this new database user name . Indicates that the user name can be registered . That third msg: If you need to return after ajax success would trigger other method in the method, you can write allrules in some method names can be, you can also enter their own string information , it will use your last prompt box customized information .

 

 

 

 

This completes the plugin ajax validation. The above analysis may not be very comprehensive , if this plugin has new features I have not used, I hope you brought up , to share to share .

 

content is not much, just in time , the above is my little reading experience, any errors, please point out that we common learning .

没有评论:

发表评论