Creating AJAX Validator Control
Imagine that you are creating a website registration form and you need to validate a Login ID or Email field. You want to make sure that the Login ID or Email entered does not already exist in the database. You could use the CustomValidator control to call a server-side function to check whether the field is unique in the database.
<asp:TextBox id="txtEmail" Runat="server" /> <asp:CustomValidator id="valEmail" ControlToValidate="txtEmail" Text="Email already exists" OnServerValidate="valEmail_ServerValidate" Runat="server" />
void valEmail_ServerValidate(Object source, ServerValidateEventArgs args) { if (EmailAlreadyExists(args.Value)) args.IsValid = false; else args.IsValid = true; }
This of course causes a postback to the server where the server-side function is executed and the page is refreshed. This can be frustrating to the user to have to enter all the fields and submit and the page refreshes only to find out that the Email address is already in the system.
To provide a better user experience we somehow need to call the server-side function from the client without causing a postback. Using AJAX (Asynchronous JavaScript and XML) we can create a validator control to call the server-side validation function from the client. The advantage of using AJAX is that no postback to the server is apparent to the user.
Our control needs to inherit from the BaseValidator class and also implement the ICallbackEventHandler interface. The BaseValidator class serves as the abstract base class for validation controls and provides the core implementation for all validation controls. The BaseValidator requires you to implement a single method:
- EvaluateIsValid – returns true when the form field being validated is valid.
The BaseValidator class also includes several other methods that you can override or otherwise use. The most useful of these methods is the following:
- GetControlValidationValue – enables you to retrieve the value of the control being validated.
When you create a custom validation control, you override the EvaluateIsValid() method and, within the EvaluateIsValid() method, you call GetControlValidationValue to get the value of the form field being validated.
public class MyAjaxValidator : BaseValidator, ICallbackEventHandler { public event ServerValidateEventHandler ServerValidate; string _controlToValidateValue; protected override bool EvaluateIsValid() { string controlToValidateValue = this.GetControlValidationValue(this.ControlToValidate); return ExecuteValidationFunction(controlToValidateValue); } private bool ExecuteValidationFunction(String controlToValidateValue) { ServerValidateEventArgs args = new ServerValidateEventArgs(controlToValidateValue, this.IsValid); if (ServerValidate != null) ServerValidate(this, args); return args.IsValid; }
The ICallbackEventHandler interface defines two methods that are called on the server when an AJAX request is made from the client:
- RaiseCallbackEvent – called to handle the event, passing the event argument as a parameter
- GetCallbackResult – returns the result of the callback
public void RaiseCallbackEvent(string eventArgument) { _controlToValidateValue = eventArgument; } public string GetCallbackResult() { return ExecuteValidationFunction(_controlToValidateValue).ToString(); }
In the OnPreRender() method, a JavaScript include file and startup script are registered.
protected override void OnPreRender(EventArgs e) { String eventRef = Page.ClientScript.GetCallbackEventReference(this, "", "", ""); String includeScript = Page.ResolveClientUrl("~/Scripts/MyAjaxValidator.js"); Page.ClientScript.RegisterClientScriptInclude("MyAjaxValidator", includeScript); String startupScript = String.Format(“document.getElementById('{0}').evaluationfunction = 'MyAjaxValidatorEvaluateIsValid';", this.ClientID); Page.ClientScript.RegisterStartupScript(this.GetType(), "MyAjaxValidator", startupScript, true); base.OnPreRender(e); } protected override bool DetermineRenderUplevel() { return Context.Request.Browser.SupportsCallback; }
The JavaScript include file contains the client-side functions that are called when the MyAjaxValidator validates a form field on the client. The startup script associates the client-side MyAjaxValidatorEvaluateIsValid() function with the MyAjaxValidator control. The client-side validation framework automatically calls this JavaScript function when performing validation.
function MyAjaxValidatorEvaluateIsValid(val) { var value = ValidatorGetValue(val.controltovalidate); WebForm_DoCallback(val.id, value, MyAjaxValidatorResult, val, MyAjaxValidatorError, true); return true; } function MyAjaxValidatorResult(returnValue, context) { if (returnValue == 'True') context.isvalid = true; else context.isvalid = false; ValidatorUpdateDisplay(context); } function MyAjaxValidatorError(message) { alert('Error: ' + message); }
The MyAjaxValidatorEvaluateIsValid() JavaScript method initiates an AJAX call by calling the WebForm_DoCallback() method. This method calls the server-side validation function associated with the MyAjaxValidator control. When the AJAX call completes, the MyAjaxValidatorResult() method is called. This method updates the display of the validation control on the client.
The following page illustrates how you can use the MyAjaxValidator control. This page handles the MyAjaxValidator control’s ServerValidate event to associate a custom validation function with the control. The validation function checks whether a email already exists in the database. If you enter a email that already exists, a validation error message is displayed. The message is displayed in the browser before you submit the form back to the server.
<form id="form1" runat="server"> <asp:Label id="lbEmail" Text="Email:" AssociatedControlID="txtEmail" Runat="server" /> <asp:TextBox id="txtEmail" Runat="server" /> <custom:MyAjaxValidator id="valEmail" ControlToValidate="txtEmial" Text="Email already exists" OnServerValidate="valEmail_ServerValidate" Runat="server" /> <br /> <!--some other controls here --> <br /> <asp:Button id="btnSubmit" Text="Submit" Runat="server" OnClick="btnSubmit_Click" /> </form>
And in code behind
protected void valEmail_ServerValidate(object source, ServerValidateEventArgs args) { if (EmailAlreadyExists(args.Value)) args.IsValid = false; else args.IsValid = true; }
It is important to realize that you can associate any server-side validation function with the MyAjaxValidator. You can perform a database lookup, call a web service, or perform a complex mathematical function. Whatever function you define on the server is automatically called on the client.
Complete code for MyAjaxValidator:
public class MyAjaxValidator : BaseValidator, ICallbackEventHandler { public event ServerValidateEventHandler ServerValidate; string _controlToValidateValue; protected override bool EvaluateIsValid() { string controlToValidateValue = this.GetControlValidationValue(this.ControlToValidate); return ExecuteValidationFunction(controlToValidateValue); } protected override void OnPreRender(EventArgs e) { String eventRef = Page.ClientScript.GetCallbackEventReference(this, "", "", ""); String includeScript = Page.ResolveClientUrl("~/Scripts/MyAjaxValidator.js"); Page.ClientScript.RegisterClientScriptInclude("MyAjaxValidator", includeScript); String startupScript = String.Format(“document.getElementById('{0}').evaluationfunction = 'MyAjaxValidatorEvaluateIsValid';", this.ClientID); Page.ClientScript.RegisterStartupScript(this.GetType(), "MyAjaxValidator", startupScript, true); base.OnPreRender(e); } protected override bool DetermineRenderUplevel() { return Context.Request.Browser.SupportsCallback; } public void RaiseCallbackEvent(string eventArgument) { _controlToValidateValue = eventArgument; } public string GetCallbackResult() { return ExecuteValidationFunction(_controlToValidateValue).ToString(); } private bool ExecuteValidationFunction(String controlToValidateValue) { ServerValidateEventArgs args = new ServerValidateEventArgs(controlToValidateValue, this.IsValid); if (ServerValidate != null) ServerValidate(this, args); return args.IsValid; } }REF:Sams ASP.Net 2.0 by Stephen Walther
Hi there, Great article and very informative. I only have one problem with this. I cannot get it to work with master pages. I\’ve searched the internet and found solutions but none describes it in enough detail for me to fix it. Seems like the WebForm DoCallback() requires the Unique ID not the ClientID to be passed to it as written in this post. Unfortunately this is not working for me. Whenever I pass the unique id it returns the control name with \’$\’ instead of \’:\’ as they say it should. Could you please explain to me what needs to be changed in your code for the validator to work in Master pages. I am using .net 3.5 and vs 2008. Thanks a lot in advanceLukas
Lukas,
Thank you for pointing out the master page issue. You can make this validator work with master pages by changing the MyAjaxValidatorEvaluateIsValid function in the javacsript file to:
var callbackId = val.id; callbackId = callbackId.replace(/_/g, "$"); WebForm_DoCallback(callbackId, value, MyAjaxValidatorResult, val, MyAjaxValidatorError, true);
OR to make this control work with or without master pages modify the PreRender to and create a seperate javascript function which does the replace to get the uniqueid:
//PreRender
String startupScript = String.Format("document.getElementById(\’{0}\’).evaluationfunction = \’MyAjaxValidatorEvaluateIsValid\’;", this.ClientID);if(Page.Master != null) startupScript = String.Format("document.getElementById(\’{0}\’).evaluationfunction = \’MyAjaxValidatorMasterPageEvaluateIsValid\’;", this.ClientID);Page.ClientScript.RegisterStartupScript(this.GetType(), "MyAjaxValidator", startupScript, true);
//javascript
function \’MyAjaxValidatorMasterPageEvaluateIsValid\'(val){ var value = ValidatorGetValue(val.controltovalidate); var callbackId = val.id; callbackId = callbackId.replace(/_/g, "$"); WebForm_DoCallback(callbackId, value, MyAjaxValidatorResult, val, MyAjaxValidatorError, true);
return true;}
Your article gives me great help, thank you very much. But here I\’ve found a bug.In the method "MyAjaxValidatorEvaluateIsValid" it returns "true" immediatelly after call the callback function. So when the page is being submited, validator scripts makes mistake.But still now, I have no idea to fix it, even though I use the synchronize callback function, the "MyAjaxValidatorEvaluateIsValid" returns without waiting…mmmmm…. Do you have any advice?Thanks again.By the way…My English is very weak…I can hardly to express, hope you can understand me, sorry.
Hi,Great blog with interesting informations. I can use it t solve my problem.ThanxM.
Seems like very nice article .but when i am trying to use it i am getting the js error as object expected. and on debugging i find out the error was coming when executing the function WebForm_DoCallback. i dont understand why WebForm_DoCallback is not getting rendered when the page is executed..any help from you guys will great for me.thanks