5.2. Data Validation In addition to providing controls for data binding, Atlas ships with its own controls for validating data the user enters, a feature that many ASP.NET developers find useful. Atlas supports the following validators:
requiredFieldValidator Checks whether the user has entered a value into a control
regexValidator Checks the data in a control against a regular expression to match a pattern
typeValidator Checks the data in a control against a data type
rangeValidator Checks the data in a control against a value range
customValidator Checks the data in a control using a custom validation function To implement data validation, you need: In the following sections you'll see how to put each of the Atlas validators to work, including how to do your own custom validation. 5.1.3. Checking a Required Field The requiredFieldValidator class, which checks whether a control contains data, is a commonly used Atlas data validator. The following markup generates both an input field and a span to display any error messages that the validator generates: <input type="text" /> <span style="color: red;">*</span> As you can see, the label for the error message is not hidden by default. Atlas takes care of hiding it automatically. In the xml-script for the page, add markup for the HTML elements taking part in the validationjust the elements, not any controls for displaying errors. In the <validators> subelement, specify the validator to use. The errorMessage property contains the text to display if validation fails. However, the Atlas validator is different than its ASP.NET counterpart. In Atlas, the value of the errorMessage property is used as a tool tip that appears when you hold the mouse pointer over the error text (that is, over the Atlas validator control). Speaking of error text, there is no equivalent for the Text property of ASP.NET validation controls. The error text appearing in the label is the text that is already there. <textBox > <validators> <requiredFieldValidator errorMessage="** TextBox1 value missing" /> </validators> </textBox> The second step is to use the <validationErrorLabel> element with the following attributes:
targetElement The ID of the control to display errors
associatedControl The ID of the element to validate A complete page with validation is shown in Example 5-6. Example 5-6. Using a validator for required fields ControlValidationRequiredField.aspx <%@ Page Language="C#" %> <!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>Atlas</title> </head> <body> <form runat="server"> <atlas:ScriptManager runat="server"> </atlas:ScriptManager> <div> <input type="text" /> <span style="color: red;">*</span> <br /> <input type="submit" /> </div> </form> <script type="text/xml-script"> <page xmlns:script="http://schemas.microsoft.com/xml-script/2005"> <components> <textBox > <validators> <requiredFieldValidator errorMessage="** TextBox1 value missing" /> </validators> </textBox> <validationErrorLabel associatedControl="TextBox1" /> </components> </page> </script> </body> </html> | Load the page, enter some data in the text field, leave the text field (which raises the change event). Then enter the field again, delete its contents, and leave the field again. For the second time the change event is raised, and this time the validation control is triggered. The error text appears; the (longer) message is displayed as a tool tip, as shown in Figure 5-4. Figure 5-4. The error text, including more information in the tool tip 5.1.4. Checking Against a Regular Expression Using a regular expression to check the validity of data works just like the ASP.NET requiredFieldValidation control, but the name of the XML element and its attributes are different. The regex property (or attribute, depending on whether you are using code or markup) provides the regular expression the validator uses to check the data: <regexValidator regex="/\d*/" errorMessage="** digits only" /> The code shown in Example 5-7 contains two validators: one checks whether there is anything in the text field, and the other one allows only digits. (You could also achieve this using a data type check.) Example 5-7. Using an Atlas validator with a regular expression ControlValidationRegex.aspx <%@ Page Language="C#" %> <!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>Atlas</title> </head> <body> <form runat="server"> <atlas:ScriptManager runat="server"> </atlas:ScriptManager> <div> <input type="text" /> <span style="color: red;">*</span> <br /> <input type="submit" /> </div> </form> <script type="text/xml-script"> <page xmlns:script="http://schemas.microsoft.com/xml-script/2005"> <components> <textBox > <validators> <requiredFieldValidator errorMessage="** TextBox1 value missing" /> <regexValidator regex="/\d*/" errorMessage="** digits only" /> </validators> </textBox> <validationErrorLabel associatedControl="TextBox1" /> </components> </page> </script> </body> </html> | 5.1.5. Checking the Data Type The <typeValidator> element checks the data type of a value. The only data type currently supported is Number, but other types are likely in future releases. The type property of the <typeValidator> element contains the data type: <typeValidator type="Number" errorMessage="** numbers only" /> The code shown in Example 5-8 uses both a requiredFieldValidator and typeValidator to check for numeric values only. Example 5-8. Using a validator for a data-type check ControlValidationType.aspx <%@ Page Language="C#" %> <!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>Atlas</title> </head> <body> <form runat="server"> <atlas:ScriptManager runat="server"> </atlas:ScriptManager> <div> <input type="text" /> <span style="color: red;">*</span> <br /> <input type="submit" /> </div> </form> <script type="text/xml-script"> <page xmlns:script="http://schemas.microsoft.com/xml-script/2005"> <components> <textBox > <validators> <requiredFieldValidator errorMessage="** TextBox1 value missing" /> <typeValidator type="Number" errorMessage="** numbers only" /> </validators> </textBox> <validationErrorLabel associatedControl="TextBox1" /> </components> </page> </script> </body> </html> | | This data type check is not a real numeric check. It just calls the JavaScript function parseInt() and determines whether the conversion fails (i.e., the result is NaN). However, strings that begin with a number, such as 123xyz, can be converted into a number (123). Therefore, this approach is not bullet-proof and you may be better off using a regular expression like this one: |
|
0|[1-9][0-9]* 5.1.6. Checking a Range Sometimes a value must not only be numeric, but must also have a value that lies within in a certain range (for instance, this is true for time intervals or dates). In that case, you can use the <rangeValidator> element. You set the lower and upper bounds in the lowerBound and upperBound properties. The following markup shows how to check for a value between 1 and 6: <rangeValidator lowerBound="1" upperBound="6" errorMessage="** 1 to 6 only" /> Example 5-9 builds on the preceding example. The data is checked not only with a requiredFieldValidator and a typeValidator, but with a rangeValidator as well. Example 5-9. Using a validator for a valid range ControlValidationRange.aspx <%@ Page Language="C#" %> <!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>Atlas</title> </head> <body> <form runat="server"> <atlas:ScriptManager runat="server"> </atlas:ScriptManager> <div> <input type="text" /> <span style="color: red;">*</span> <br /> <input type="submit" /> </div> </form> <script type="text/xml-script"> <page xmlns:script="http://schemas.microsoft.com/xml-script/2005"> <components> <textBox > <validators> <requiredFieldValidator errorMessage="** TextBox1 value missing" /> <typeValidator type="Number" errorMessage="** numbers only" /> <rangeValidator lowerBound="1" upperBound="6" errorMessage="** 1 to 6 only" /> </validators> </textBox> <validationErrorLabel associatedControl="TextBox1" /> </components> </page> </script> </body> </html> | 5.1.7. Custom Validation To achieve the greatest flexibility, you can write a custom function to validate user data: a custom validator. The signature for your validation function is as follows: function <name>(sender, args) { } From the second parameter, the value to validate can be retrieved using get_value(). After validation, you call the set_isValid() method. If validation succeeds, pass true as parameter; otherwise, pass false. Let's imagine that for some inexplicable reason, only square numbers may now be entered into the text field. The following function does the validation: function validateSquare(sender, args) { var value = args.get_value(); args.set_isValid(Math.sqrt(value) == Math.floor(Math.sqrt(value))); } In the xml-script, the <customValidator> element must include a validateValue attribute that references the custom validation function: <customValidator validateValue="validateSquare" errormessage="** square numbers only" /> The Visibility Mode of a Validation Control One property of validation controlsor, to be exact, of <validationErrorLabel> elementshas not been covered yet: visibilityMode. Two values are possible (via the Sys.UI.VisibilityMode enumeration): The display style (or in JavaScript: element.style.display) is set to this mode. If no visibility mode is provided, "none" is used. This controls how the validation error Label control is hidden when the page has been loaded. |
Example 5-10 shows the complete code for this custom validator. Example 5-10. Using a custom validator ControlValidationCustom.aspx <%@ Page Language="C#" %> <!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>Atlas</title> <script language="JavaScript" type="text/javascript"> function validateSquare(sender, args) { var value = args.get_value(); args.set_isValid(Math.sqrt(value) == Math.floor(Math.sqrt(value))); } </script> </head> <body> <form runat="server"> <atlas:ScriptManager runat="server"> </atlas:ScriptManager> <div> <input type="text" /> <span style="color: red;">*</span> <br /> <input type="submit" /> </div> </form> <script type="text/xml-script"> <page xmlns:script="http://schemas.microsoft.com/xml-script/2005"> <components> <textBox > <validators> <requiredFieldValidator errorMessage="** TextBox1 value missing" /> <typeValidator type="Number" errorMessage="** numbers only" /> <customValidator validateValue="validateSquare" errorMessage="** square numbers only" /> </validators> </textBox> <validationErrorLabel associatedControl="TextBox1" /> </components> </page> </script> </body> </html> | 5.1.8. Programmatic Validation The declarative approach fares well in practice, but there is a programmatic approach to validation as well. You do need some declarations for it, though, like this: <textBox > </textBox> <validationErrorLabel associatedControl="TextBox1" /> You can then create the validator using JavaScript code. It is not easy to guess how it is done. Two steps are required: Add the validator: element.get_validators().add(validator). If you want to use a callback function (a function being called when the validation has occurred), use : element.validated.add(function). To access the element to validate, you cannot get an instance of the control with the usual new Sys.UI.XXX approach. Instead, you must use the stranger-looking syntax that you have saw when we covered preventing form submissions: var textbox = $("TextBox1").control; So, you are accessing the client-side element using the dollar sign and then accessing its control property. Example 5-11 shows a complete page that uses a programmatic approach to validation. Example 5-11. Programmatically using a custom validator ControlValidationCustomProgrammatic.aspx <%@ Page Language="C#" %> <!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>Atlas</title> <script language="JavaScript" type="text/javascript"> function pageLoad() { var textbox = $("TextBox1").control; validator = new Sys.UI.RequiredFieldValidator(); validator.set_errorMessage("** enter some data"); textbox.get_validators().add(validator); textbox.validated.add(validationComplete); } function validationComplete(sender, args) { } </script> </head> <body> <form runat="server"> <atlas:ScriptManager runat="server"> </atlas:ScriptManager> <div> <input type="text" /> <span style="color: red;">*</span> <br /> <input type="submit" /> </div> </form> <script type="text/xml-script"> <page xmlns:script="http://schemas.microsoft.com/xml-script/2005"> <components> <textBox > </textBox> <validationErrorLabel associatedControl="TextBox1" /> </components> </page> </script> </body> </html> | This of course also works for more complex validators, including the custom validator. In this particular case, the syntax for declaring the custom validation function is the following: validator.validateValue.add(validation function); Example 5-12 demonstrates how to combine both declarative validators and programmatic validators. Example 5-12. Using declarative and programmatic validators ControlValidationRequiredFieldProgrammatic.aspx <%@ Page Language="C#" %> <!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>Atlas</title> <script language="JavaScript" type="text/javascript"> function validateSquare(sender, args) { var value = args.get_value(); args.set_isValid(Math.sqrt(value) == Math.floor(Math.sqrt(value))); } function pageLoad() { var textbox = $("TextBox1").control; validator = new Sys.UI.CustomValidator(); validator.set_errorMessage("Square numbers only"); validator.validateValue.add(validateSquare); textbox.get_validators().add(validator); textbox.validated.add(validationComplete); } function validationComplete(sender, args) { } </script> </head> <body> <form runat="server"> <atlas:ScriptManager runat="server"> </atlas:ScriptManager> <div> <input type="text" /> <span style="color: red;">*</span> <br /> <input type="submit" /> </div> </form> <script type="text/xml-script"> <page xmlns:script="http://schemas.microsoft.com/xml-script/2005"> <components> <textBox > <validators> <requiredFieldValidator errorMessage="** TextBox1 value missing" /> <typeValidator type="Number" errorMessage="** numbers only" /> </validators> </textBox> <validationErrorLabel associatedControl="TextBox1" /> </components> </page> </script> </body> </html> | 5.1.9. Validation Groups Validation controls can also be grouped by creating a <validationGroup> element that groups the controls you want to validate as a unit. All the validators in a validation group perform their test individually, but then you can test the group as a whole: if any of the validation checks failed, then the group has failed; if all the controls validate, then the group passes. Grouping is particularly useful for being able to enable and disable sets of validators conditionally. The validation group exposes a method isValid() to determine whether the validation failed or not. This can be used in conjunction with data binding to display a message when the validation succeeds or fails. First of all, you must provide an element to display the message: <div >-no errors-</div> Then you can bind this element's visible property to the validation group's isValid() method. In that case, the <div> element will be visible if all the validators in the group have passed. <label > <bindings> <binding dataContext="group" dataPath="isValid" property="visible" /> </bindings> </label> Now, to make the <div> element visible only if the validation fails, use the Invert transformer: <binding dataContext="group" dataPath="isValid" property="visible" transform="Invert" /> So, only one thing is missing: the validation group itself. It is represented by the <validationGroup> element. It needs an ID (the preceding markup used "group"), and within the group element, all form elements that take part in the validation are referenced, like this: <validationGroup > <associatedControls> <reference component="TextBox1" /> <reference component="TextBox2" /> </associatedControls> </validationGroup> Example 5-13 shows a page with a validation group. In the page, the <div> element displays -no errors- when all the text boxes have passed validation. (Because the first text box has a required field validator, the <div> element is displayed only when that text box is filled in and the second text box contains a numeric value that's a square number.) Example 5-13. Using a validation group bound to a label CustomValidationGroup.aspx <%@ Page Language="C#" %> <!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>Atlas</title> <script language="JavaScript" type="text/javascript"> function validateSquare(sender, args) { var value = args.get_value(); args.set_isValid(Math.sqrt(value) == Math.floor(Math.sqrt(value))); } </script> </head> <body> <form runat="server"> <atlas:ScriptManager runat="server"> </atlas:ScriptManager> <div> Anything: <input type="text" /> <span style="color: red;">*</span> <br /> A square: <input type="text" /> <span style="color: red;">*</span> <br /> <input type="submit" /> </div> <div >-no errors-</div> </form> <script type="text/xml-script"> <page xmlns:script="http://schemas.microsoft.com/xml-script/2005"> <components> <textBox > <validators> <requiredFieldValidator errorMessage="** TextBox1 value missing" /> </validators> </textBox> <validationErrorLabel associatedControl="TextBox1" /> <textBox > <validators> <requiredFieldValidator errorMessage="** TextBox2 value missing" /> <typeValidator type="Number" errorMessage="** numbers only" /> <customValidator validateValue="validateSquare" errorMessage="** square numbers only" /> </validators> </textBox> <validationErrorLabel associatedControl="TextBox2" /> <validationGroup > <associatedControls> <reference component="TextBox1" /> <reference component="TextBox2" /> </associatedControls> </validationGroup> <label > <bindings> <binding dataContext="group" dataPath="isValid" property="visible" /> </bindings> </label> </components> </page> </script> </body> </html> | Figure 5-5 shows the result. Figure 5-5. The label appears only when all text boxes are filled correctly |