Introduction
Many times, it is desirable to perform JavaScript validation before submitting a page to the web server. For simple validations, client side JavaScript validation is sufficient. However, in complex scenarios, server side validation may be required and client side JavaScript will not suffice. In such scenarios, typically postbacks must be performed in order to do the server side validation. In many cases, the developer may not want the page to be posted back for validations, because the page state may be lost and the user experience may not be the smoothest. This is where AJAX comes in. The present article builds upon the power of AJAX to perform server side validations through the use of JavaScript without relying on postbacks.
Background
As mentioned before, the primary idea behind presenting this article is to show how to perform server side validations even when complex scenarios arise, while avoiding postbacks. The below control can be used as a centralized validation component, which can perform all validations for an ASP.NET web site.
Ajax Examples
Before getting into the details of how the validation works, we shall demonstrate how to make a simple AJAX call. For more information on Ajax, please see An Introduction to AJAX Techniques and Frameworks for ASP.NET.
In our examples, we will be using Ajax.NET component, developed by Michael Schwartz. We chose this component because it is very easy to implement and offers a great amount of flexibility.
The following settings need to be applied to the web.config in order to use Ajax.NET component successfully. For a detailed explanation of these settings, please see Michael Schwartz's web site Ajax.NET.
<configSections>
<sectionGroup name="ajaxNet">
<section name="ajaxSettings"
type="AjaxPro.AjaxSettingsSectionHandler,AjaxPro.2"
requirePermission="false" restartOnExternalChanges="true"/>
</sectionGroup>
</configSections>
<ajaxNet>
<ajaxSettings>
<urlNamespaceMappings useAssemblyQualifiedName="false">
</urlNamespaceMappings>
<jsonConverters></jsonConverters>
<debug enabled="false" />
<token enabled="false" sitePassword="password" />
<oldStyle>
<includeMsPrototype/>
</oldStyle>
</ajaxSettings>
</ajaxNet>
<location path="ajaxpro">
<system.web>
<httpHandlers>
<add verb="*" path="*.ashx"
type="AjaxPro.AjaxHandlerFactory,AjaxPro.2"/>
</httpHandlers>
</system.web>
</location>
Making a Simple Ajax Call
Create a simple web page with an HTML text box and a button control. Notice that the onclick
event of the button calls the JavaScript function makeAjaxCall()
which invokes Ajaxsample.AjaxTest
Ajax method in code behind.
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Ajax.NET Sample</title>
<script>
function makeAjaxCall()
{
var retVal = AjaxSample.AjaxTest(document.form1.txtTest.value)
alert(retVal.value)
}
</script>
</head>
<body>
<form id="form1" runat="server">
<!--
Enter value:<input type="text" id="txtTest"/>
<!--
<input type="button" id="btnTest" value="Test Ajax"
onclick="makeAjaxCall()" />
</form>
</body>
</html></span>
The code behind class should be marked with <AjaxPro.AjaxNamespace("AjaxSample")>
and the Ajax web method to be invoked should be marked with <AjaxPro.AjaxMethod()>
. Also the page load event should register the namespace AjaxSample
using AjaxPro.Utility.RegisterTypeForAjax(GetType(AjaxSample), Page)
. Below is the snippet of code showing the code behind.
<AjaxPro.AjaxNamespace("AjaxSample")> Partial Public Class AjaxSample
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Me.Load
AjaxPro.Utility.RegisterTypeForAjax(GetType(AjaxSample), Page)
End Sub
<AjaxPro.AjaxMethod()>
Public Function AjaxTest(ByVal testParam As String) As String
Return "You sent '" & testParam & "' as the testParam."
End Function
End Class
Below is the output for the sample page created.
Building the Validation Control
We create a web user control so that the same control can be reused across all the web pages in the application. The validations that will be supported by this control are defined inside a Enum ValidationType
. This can be extended to support additional validations that are not covered.
Public Enum ValidationType
RequiredField = 1
DateRangeValidation = 4
SingleDateValidation = 2
CreditCardNumberValidation = 3
DropdownlistValidation = 5
DropdownlistRangeValidation = 11
EmailValidation = 7
PostalCodeValidation = 8
URLValidation = 9
TimeSlotRangeValidation = 10
Numeric = 12
HoursValidation = 13
MinutesValidation = 14
CompareValidation = 15
TimeSlotValidation = 16
End Enum
Mark the controls class with <AjaxPro.AjaxNamespace("AjaxValidator")>
, methods with <AjaxPro.AjaxMethod()>
and finally register the namespace in the page load event with AjaxPro.Utility.RegisterTypeForAjax(GetType(Validator), Page)
where Validator
is the class name of the user control. This is similar to what we did in the earlier sample.
Create a structure RegisteredControlsDs
and a private
member of list class to contain the list of the web controls of type RegisteredControlsDs
to be validated.
Private Structure RegisteredControlsDs
Public control1 As UI.Control
Public control1Desc As String
Public control2 As UI.Control
Public control2Desc As String
Public type As ValidationType
Public isRequired As Boolean
Public Sub New(ByVal control1 As UI.Control,
ByVal control1Desc As String,
ByVal control2 As UI.Control,
ByVal control2Desc As String,
ByVal type As ValidationType,
ByVal isRequired As Boolean)
Me.control1 = control1
Me.control1Desc = control1Desc
Me.control2 = control2
Me.control2Desc = control2Desc
Me.type = type
Me.isRequired = isRequired
End Sub
Public Sub New(ByVal control1 As UI.Control,
ByVal control1Desc As String,
ByVal type As ValidationType,
ByVal isRequired As Boolean)
Me.control1 = control1
Me.control1Desc = control1Desc
Me.control2 = control1
Me.control2Desc = control1Desc
Me.type = type
Me.isRequired = isRequired
End Sub
End Structure
Private _registeredControlsList As New List(Of
RegisteredControlsDs)
The controls to be validated should be added to _registeredControlsList
list. Depending on whether validation is on a single control or between two controls, one of the below two methods are used to register the controls respectively. For example, if it's comparison between two controls, then we use the second method. If it's a single control that needs to be validated for say Date
, then we use the first method for registering the control.
//Method 1
Public Sub RegisterControl(ByVal control As UI.Control, _
ByVal controlDesc As String, _
ByVal type As ValidationType, _
ByVal isRequired As Boolean)
_registeredControlsList _
.Add(New RegisteredControlsDs(control, controlDesc, type, isRequired))
End Sub
//Method 2
Public Sub RegisterControls(ByVal control1 As UI.Control, _
ByVal control1Desc As String, _
ByVal control2 As UI.Control, _
ByVal control2Desc As String, _
ByVal type As ValidationType, _
ByVal isRequired As Boolean)
_registeredControlsList_
.Add(New RegisteredControlsDs(control1, control1Desc,_
control2, control2Desc, type, isRequired))
End Sub
Once the controls are registered using the above methods, the next step is to create a ValidateControls
method to validate the controls. Based on whether it is a single control validation or validation between two controls, ValidateControls
calls one of the ValidateControl
methods which is overloaded. These methods return a ValidationResult
datastructure.
<AjaxPro.AjaxMethod()>
Public Function ValidateControls(ByVal ds As DataSet) As ValidationResult
Dim retVal As New ValidationResult(True)
Try
Dim dt As DataTable = ds.Tables(0)
Dim firstBadControl As String = String.Empty
For Each dr As DataRow In dt.Rows
Dim val1Name As String = dr("Control1Name")
.ToString()
Dim val1 As String = dr("Control1Value").ToString()
Dim val1Desc As String = dr("Control1Description")
.ToString()
Dim val2Name As String = dr("Control2Name")
.ToString()
Dim val2 As String = dr("Control2Value").ToString()
Dim val2Desc As String = dr("Control2Description")
.ToString()
Dim isreq As Boolean = Boolean.Parse(dr(
"IsRequired").ToString)
Dim type As ValidationType = CType(dr(
"ValidationType"), ValidationType)
Dim validationResult As ValidationResult
If val1Name = val2Name Then
validationResult = ValidateControl(val1Name,
val1Desc, val1, isreq, type)
Else
validationResult = ValidateControl(val1Name,
val1Desc, val1, val2Name,
val2Desc, val2, isreq, type)
End If
If Not validationResult.Status Then
retVal.Message &= validationResult
.Message & Environment.NewLine
retVal.Status = False
If firstBadControl = String.Empty Then
firstBadControl = val1Name
End If
Next
retVal.InvalidControlName = firstBadControl
Catch ex As Exception
retVal.Message = "Ajax Exception:" & ex.ToString()
retVal.Status = False
End Try
Return retVal
End Function
Finally, RegisterScript
method builds the JavaScript dynamically based on the registered controls on the server side.
Public Sub RegisterScript()
Dim scriptCode As String = _
"<script language="
"function _validatePage()" & _
"{" & _
" try {" & _
"var ds = new Ajax.Web.DataSet();" & _
"var dt = new Ajax.Web.DataTable();" & _
"dt.addColumn('Control1Name', 'System.String');" & _
"dt.addColumn('Control1Value', 'System.String');" & _
"dt.addColumn('Control1Description', 'System.String');" & _
"dt.addColumn('Control2Name', 'System.String');" & _
"dt.addColumn('Control2Value', 'System.String');" & _
"dt.addColumn('Control2Description', 'System.String');" & _
"dt.addColumn('IsRequired', 'System.Boolean');" & _
"dt.addColumn('ValidationType', 'System.Int32');" & _
"ds.addTable(dt);" & _
"<DELTA>" & _
"var res = AjaxValidator.ValidateControls(ds);" & _
"if (res.value.Status == false) " & _
" {alert(res.value.Message);" & _
"document.getElementById(res.value.InvalidControlName).focus();" & _
"return false;" & _
"} else return true;" & _
"} catch(ex) {alert('Validation Exception:' + ex.description); " &_
"return false; }" & _
"}" & _
"</script>"
Dim nl As String = Environment.NewLine
Dim deltaCode As String = nl & "var <r> = {};" & nl & _
"<r>.Control1Name = '<hidControlValue1>';" & nl & _
"<r>.Control1Value = document.getElementById('<hidControlValue1>')." &_
"value;" & nl & _
"<r>.Control1Description = '<hidControlDesc1>';" & nl & _
"<r>.Control2Name = '<hidControlValue2>';" & nl & _
"<r>.Control2Value = document.getElementById('<hidControlValue2>')." &_
"value;" & nl & _
"<r>.Control2Description = '<hidControlDesc2>';" & nl & _
"<r>.IsRequired = <isRequired>;" & nl & _
"<r>.ValidationType = <type>;" & nl & _
"dt.addRow(<r>);" & nl
deltaCode = JsTryCatchIt(deltaCode, _
"Unable to find control <r>:<hidControlDesc1>")
Dim codeBuilder As New StringBuilder
For i As Integer = 0 To _registeredControlsList.Count - 1
Dim snippet As String = deltaCode
With _registeredControlsList(i)
snippet = snippet.Replace("<r>", "r" & i)
snippet = snippet.Replace("<hidControlDesc1>", .control1Desc)
snippet = snippet.Replace("<hidControlDesc2>", .control2Desc)
snippet = snippet.Replace("<isRequired>", _
.isRequired.ToString.ToLower)
snippet = snippet.Replace("<type>", _
CType(.type, Integer).ToString())
snippet = snippet.Replace("<hidControlValue1>", _
GetClientId(.control1))
snippet = snippet.Replace("<hidControlValue2>", _
GetClientId(.control2))
End With
codeBuilder.Append(snippet)
Next
scriptCode = scriptCode.Replace("<DELTA>", codeBuilder.ToString)
Page.ClientScript.RegisterStartupScript(Me.GetType, "validatorScript", _
scriptCode)
AppendToAttribute(_triggerControl, "onclick", _
"if(!_validatePage()) return false;")
End Sub
With this, the creation of validation user control is done. The below section describes how to use the validation control in our application pages, which is very easy.
Putting It All Together: Using the Validation User Control in a Web Page
Create a web form AjaxValidationSample.aspx and drag the validation user control into it. Add as many controls as needed for validation. In the page load event of the form, call the RegisterValidationControls()
method. Inside RegisterValidationControls()
(or a method of your choosing) you need to provide a Trigger Control which will trigger the validation. After that, call RegisterControl
or RegisterControls
methods in order to register the control(s) you want. At the end, call the RegisterScript
method to render the Ajax JavaScript.
The following code shows the contents of AjaxValidationSample.aspx and its code behind:
HTML code:
<%@ Page Language="vb" AutoEventWireup="false"
CodeBehind="AjaxValidationSample.aspx.vb"
Inherits="AjaxValidation.AjaxValidationSample" %>
<%@ Register Src="Validator.ascx" TagName="Validator" TagPrefix="uc1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Ajax Validation Sample</title>
</head>
<body>
<form id="form1" runat="server">
<table>
<tr>
<td colspan="2">
<uc1:Validator ID="Validator1" runat="server" />
</td>
</tr>
<tr>
<td>Email Address:</td>
<td><asp:TextBox ID="txtEmail" runat="server"
ToolTip="Email Address"></asp:TextBox></td>
</tr>
<tr>
<td>Date Range:</td>
<td><asp:TextBox ID="txtFrom" runat="server"
ToolTip="From Date"/><asp:TextBox ID="txtTo"
runat="server" ToolTip="To Date"/></td>
</tr>
</table>
<asp:Button ID="btnSubmit" Text="Submit" runat="server"/>
</form>
</body>
</html>
Code behind:
Public Partial Class AjaxValidationSample
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object,_
ByVal e As System.EventArgs) Handles Me.Load
RegisterValidationControls()
End Sub
Private Sub RegisterValidationControls()
Validator1.TriggerControl = btnSubmit
Validator1.RegisterControl(txtEmail, ValidationType.EmailValidation, _
True)
Validator1.RegisterControls(txtFrom, txtTo, _
ValidationType.DateRangeValidation, True)
Validator1.RegisterScript()
End Sub
End Class
That's it! At runtime, when the form loads, the JavaScript is loaded and the validations are applied. Below is the screen shot of the output once the submit button is clicked.
Scope
The presented validation control is designed to handle single control validation and validation between two controls in batch mode. This means that you can validate 10 controls with one Ajax call.
Conclusion
We have seen how to create an extensible AJAX based validation user control. It is very easy to keep adding more types of validations to this control. All that one needs to do is define a new enum
and provide the validation code and the validation control will do the rest. Hope this will be useful to someone. Happy coding!
History
- Jan 2, 2007 - Initial version