研究了一下Asp.net Validator Control在Client Side的实现:
1. Page 在 Browser load完后会依次执行:
1) var Page_Validators = new Array(document.getElementById("RequiredFieldValidator1"), ...);
var Page_ValidationSummaries = new Array(document.getElementById("ValidationSummary1"));
2) validator = document.all ? document.all["RequiredFieldValidator1"] : document.getElementById("RequiredFieldValidator1");
.controltovalidate = "TextBox1";
.focusOnError = "t";
.errormessage = "Required";
.display = "Dynamic";
.evaluationfunction = "RequiredFieldValidatorEvaluateIsValid";
.clientvalidationfunction = "validateTA";
.initialvalue = "";
.maximumvalue = "100";
.minimumvalue = "1";
.controltocompare = "TextBox3";
.validationexpression = "testGP";
.validationGroup = "^\\d{3}$";
注意: 这里的validator仅仅是页面众多的验证控件之一(即页面会初始化很多的这么个些validator),这里列举仅作为一个实例来研究。另外,以上列举中的附加属性,是合计所有验证控件常见的属性,而不是每个validator都有以上全部属性(更准确说,每个validator仅拥有其中的不同部分)。
3) var Page_ValidationActive = false;
4) call ValidatorOnLoad()
--> val:
.evaluationfunction
.isvalid
.enabled
.controltovalidate *
.controlhookup *
--> Page_ValidationActive = true;
2. 跟踪Page中执行的ValidatorOnLoad方法到Asp.net动态生成的Resource文件(Resource1),依次会执行:
ValidatorOnLoad():
--> ValidatorHookupControlID(val.controltovalidate, val) / ValidatorHookupControlID(val.controlhookup, val)
--> ValidatorHookupControl(ctrl, val)
--> ValidatorHookupEvent(control, eventType, functionPrefix)
*** ValidatedControlOnBlur(event) <--> Page_InvalidControlToBeFocused
*** ValidatorOnChange(event) <--> ValidatorValidate(vals[i], null, event) * + ValidatorUpdateIsValid() *
*** ValidatedTextBoxOnKeyPress(event) <--> ValidatorOnChange(event) + AllValidatorsValid(vals) *
ValidatorValidate(val, validationGroup, event):
--> IsValidationGroupMatch(val, validationGroup);
--> val.isvalid = val.evaluationfunction(val);
<--> RequiredFieldValidatorEvaluateIsValid
<--> RangeValidatorEvaluateIsValid
<--> RegularExpressionValidatorEvaluateIsValid
<--> CompareValidatorEvaluateIsValid
<--> CustomValidatorEvaluateIsValid
--> Page_InvalidControlToBeFocused == null ?
--> ValidatorSetFocus(val, event); *
--> ValidatorUpdateDisplay(val); *
ValidatorUpdateIsValid():
--> Page_IsValid = AllValidatorsValid(Page_Validators);
AllValidatorsValid(validators):
--> return true/false;
IsValidationGroupMatch(control, validationGroup):
--> return (controlGroup == validationGroup);
ValidatorSetFocus(val, event):
--> IsInVisibleContainer(ctrl) ?
--> ctrl.focus() + Page_InvalidControlToBeFocused = ctrl;
ValidatorUpdateDisplay(val):
--> val.style.display = val.isvalid ? "none" : "inline";
--> val.style.visibility = val.isvalid ? "hidden" : "visible";
RequiredFieldValidatorEvaluateIsValid(val):
--> return (ValidatorTrim(ValidatorGetValue(val.controltovalidate)) != ValidatorTrim(val.initialvalue)) *
RangeValidatorEvaluateIsValid(val):
--> value = ValidatorGetValue(val.controltovalidate);
--> ValidatorTrim(value);
--> ValidatorCompare(value, val.minimumvalue, "GreaterThanEqual", val) && ValidatorCompare(value, val.maximumvalue, "LessThanEqual", val)
RegularExpressionValidatorEvaluateIsValid(val):
--> value = ValidatorGetValue(val.controltovalidate);
--> ValidatorTrim(value);
--> rx = new RegExp(val.validationexpression);
--> matches = rx.exec(value);
CompareValidatorEvaluateIsValid(val):
--> value = ValidatorGetValue(val.controltovalidate);
--> ValidatorTrim(value);
--> ValidatorCompare(value, compareTo, operator, val);
CustomValidatorEvaluateIsValid(val):
--> value = ValidatorGetValue(val.controltovalidate);
--> eval(val.clientvalidationfunction + "(val, args) ;");
--> return args.IsValid;
ValidatorTrim(s):
--> var m = s.match(/^\s*(\S+(\s+\S+)*)\s*$/);
--> return (m == null) ? "" : m[1];
ValidatorGetValue(id):
--> return control.value;
--> return ValidatorGetValueRecursive(control); *
ValidatorCompare(operand1, operand2, operator, val):
--> ValidatorConvert(operand1, dataType, val) *
--> return op1 !<>= op2;
ValidatorGetValueRecursive(control):
--> return control.value;
--> return val = ValidatorGetValueRecursive(control.childNodes[i]);
ValidatorConvert(op, dataType, val):
--> dataType == "Integer/Double/Currency/Date" ?
--> return (isNaN(num) ? null : num); (*)
注意:以上2部分仅仅是页面加载过程中,通过js为页面中的control和validator建立关联,并且绑定相关client event,而没有做任何validation。
3. 回到页面源码,查看页面剩下的重要部分:
1) <form name="form1" method="post" action="default2.aspx" onsubmit="javascript:return WebForm_OnSubmit();" id="form1">
2) var theForm = document.forms['form1'] ? document.forms['form1'] : document.form1;
3) <input type="submit" name="btnOK" value="submit" onclick="javascript:WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions("btnOK", "", true, "testGP", "", false, false))" id="btnOK" />
4)function __doPostBack(eventTarget, eventArgument) {
if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
theForm.__EVENTTARGET.value = eventTarget;
theForm.__EVENTARGUMENT.value = eventArgument;
theForm.submit();
}
}
function WebForm_OnSubmit() {
if (typeof(ValidatorOnSubmit) == "function" && ValidatorOnSubmit() == false) return false;
return true;
}
function ValidatorOnSubmit() {
if (Page_ValidationActive) {
return ValidatorCommonOnSubmit();
}
else {
return true;
}
}
从3)中input(1个button)的以下部分开始跟踪,依次执行:
onclick="javascript:WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions("btnOK", "", true, "testGP", "", false, false))" -->
WebForm_DoPostBackWithOptions(options):
--> WebForm_PostBackOptions options; *
--> validationResult = Page_ClientValidate(options.validationGroup); [*--> Resource1]
--> theForm.action = options.actionUrl;
--> lastFocus = theForm.elements["__LASTFOCUS"]; [?]
lastFocus.value = options.eventTarget / active.id / active.name;
--> __doPostBack(options.eventTarget, options.eventArgument);
WebForm_PostBackOptions(eventTarget, eventArgument, validation, validationGroup, actionUrl, trackFocus, clientSubmit):
--> this.eventTarget = eventTarget;
--> ...
Page_ClientValidate(validationGroup):
--> Page_InvalidControlToBeFocused = null;
--> ValidatorValidate
--> ValidatorUpdateIsValid
--> ValidationSummaryOnSubmit(validationGroup); *
--> Page_BlockSubmit = !Page_IsValid;
--> return Page_IsValid;
ValidationSummaryOnSubmit(validationGroup):
--> Page_ValidationSummaries
--> Page_IsValid
--> IsValidationGroupMatch
--> summary.displaymode == "List/BulletList/SingleParagraph"
可以看到,如果点击button,validators如果发现invalid部分,显示相关invalid messages。然后执行form的submit方法。跟踪
onsubmit="javascript:return WebForm_OnSubmit();" 依次执行:
WebForm_OnSubmit():
--> return ValidatorOnSubmit();
--> Page_ValidationActive
--> ValidatorCommonOnSubmit(); *
ValidatorCommonOnSubmit():
--> Page_InvalidControlToBeFocused = null;
--> result = !Page_BlockSubmit; *******<Page_BlockSubmit>********
--> window.event.returnValue = result;
--> Page_BlockSubmit = false;
从上可以看到:form根据validators判断结果的另外一个全局变量Page_BlockSubmit来做一些事情,取消页面提交。如果页面IsValid,会执行__doPostBack方法,相关参数也被PostBack到Server Side,一个新的Asp.net生命周期就会开始(这是Server端的事情了,不再详叙)。