Requirements 

     
Prerequisite knowledge
Working knowledge of ActionScript 3.0.
 
User level
Beginning
Required products
Flex Builder (Download trial
 
Sample files  

   

 
Additional Requirements

Note: You can import this archive using Import > Flex Builder > Flex Project.
 
By validating user input you can not only enforce business rules, but also improve application security, preventing invalid or even harmful data from being sent to the server. For example, a validator can ensure that a Social Security Number contains nine digits or is in the format NNN-NN-NNNN. Flex provides a set of built-in validators for many types of input data, including e-mail addresses, phone numbers, and ZIP codes, to list a few.
 
In addition, with some relatively simple ActionScript, you can extend the Flex Validator class to meet specialized needs. Thus, you can validate any Flex form field against custom business rules without relying on server-side code.
 
This article shows how to create a custom validator and use it in a Flex Form container. In the process, it demonstrates the power of ActionScript subclasses.
 

 
Creating the ActionScript class

Create a new project in Flex Builder, and name it CustomValidator (see Figure 1).
 
The New Flex Project wizard
Figure 1. The New Flex Project wizard
After creating the project, choose File > New > ActionScript Class (see Figure 2). The wizard will ask for the package and class names.
 
Figure 2. The New ActionScript Class wizard
Figure 2. The New ActionScript Class wizard
If you leave the Package field empty, the wizard will display a warning, "The use of the default package is discouraged." I strongly recommend that you provide a package name. Packages enable you to organize your code in hierarchical structures, making it easier to find and use. Many organizations use their website address in reverse to begin the package name; if your company's web address is www.mycompany.com, the package name would be something like com.mycompany.packageName. In this project, you can use the package name MyUtilities.
 
You'll be using this custom validator to check filename extensions provided by the user in a TextInput control, limiting them to a few web image formats: .jpg, .jpeg, .gif, and .png. You can use FileExtensionValidator for the class name. You'll take advantage of the built-in Flex Validator superclass, which comes with very useful validation properties and methods. By subclassing – or extending – the Validator superclass, you'll have access to all its functionality, plus you can add your own.
 
In the wizard, click the Browse button next to the Superclass field; then scroll down and choose "Validator - mx.validators". Leave the other fields at their default values and click OK. Flex generates the class file, FileExtensionValidator.as, and places it in the src folder, under the MyUtilities directory (to indicate package hierarchy, much as in Java).
 
The generated class will look like this:
 
package MyUtilities { import mx.validators.Validator; public class FileExtensionValidator extends Validator { public function FileExtensionValidator() { super(); } } }

 
Customizing the new validator class

Begin by adding some variables and functions (also referred to as methods) to the new class. You'll notice the method super() called from the FileExtensionValidator() constructor. This calls the constructor of the base (Validator) superclass.
 
Also note the /** */ style comments. They provide different functionality than the single-line (//) or multiline comments (/* */). You can use ASDoc, a command-line tool, to parse these comments and generate API language reference documentation as HTML pages. In fact, the Adobe Flex team uses the ASDoc tool to generate the Adobe Flex Language Reference.
 
package MyUtilities { // These import statements enable easy referencing of // a few packages used by this class. import mx.utils.StringUtil; import mx.validators.ValidationResult; import mx.validators.Validator; /** * The FileExtensionValidator class validates a file's extension * against an array of valid extensions. * * @see mx.validators.Validator */ public class FileExtensionValidator extends Validator { /** * Indicates the error code. */ private const ERROR_BAD_EXTENSION:String = "BadFileExtension"; /** * Array stores the return value of doValidation(). */ private var _results:Array; /** * Class constructor. */ public function FileExtensionValidator() { // Call the base class' constructor. super(); } private var _validExtensions:Array; /** * Array contains valid file extensions. */ public function get validExtensions():Array { return _validExtensions; } /** * @private */ public function set validExtensions(value:Array):void { _validExtensions = value; } private var _errorMessage:String; /** * Provides a custom error message. */ public function get errorMessage():String { return _errorMessage; } /** * @private */ public function set errorMessage(value:String):void { _errorMessage = value; } } }
The ERROR_BAD_EXTENSION constant holds the error code string; you use the const keyword to indicate that this is a constant with a fixed value. As standard practice, constants are declared in capital letters, with words separated by an underscore (_). Constants make your job easier; for example, if you have multiple locations in code where you check variables against a value, a constant ensures you don't misspell the value in any of those places. In other words, if you use the "BadFileExtension" string on various lines, you might mistype it as "BadFileExtenson" or "BadFileExtesion". By eliminating such mistakes, constants make your code safer.
 
The results array enables the class to store and return any validation errors that are encountered. The validExtensions array and errorMessage string variables make the class more dynamic, enabling the calling code to provide an arbitrary list of file extensions and an error message, instead of hard-coding the values.
 
But you might be wondering: Why are these variables marked as private? The private fields and public get and set methods enable encapsulation: You hide your class implementation while providing a consistent interface to those who call your code. This way, you could later change the design of the class without affecting its users. The underscores at the beginning of the names, though not required, are standard practice; they prevent the Flex compiler from getting confused about whether you're referencing the function or the variable of the same name.
 
Note the @private tag inside the comments above each set method. When defining getter and setter methods, you use one ASDoc comment for both, marking the setter method with @private. This omits it from the generated documentation; thus, validExtensions and errorMessage are treated as if they are properties rather than methods.
 
Now you're ready to get to the heart of the class, the doValidation() method.
 
package MyUtilities { import mx.utils.StringUtil; import mx.validators.ValidationResult; import mx.validators.Validator; /** * The FileExtensionValidator class validates a file's extension * against an array of valid extensions. * * @see mx.validators.Validator */ public class FileExtensionValidator extends Validator { /** * Indicates the error code. */ private const var ERROR_BAD_EXTENSION:String = "BadFileExtension"; /** * Array stores the return value of doValidation(). */ private var _results:Array; /** * Class constructor. */ public function FileExtensionValidator() { // Call the base class' constructor. super(); } private var _validExtensions:Array; /** * Array contains valid file extensions. */ public function get validExtensions():Array { return _validExtensions; } /** * @private */ public function set validExtensions(value:Array):void { _validExtensions = value; } private var _errorMessage:String; /** * Provides a custom error message. */ public function get errorMessage():String { return _errorMessage; } /** * @private */ public function set errorMessage(value:String):void { _errorMessage = value; } /** * Overrides the superclass' doValidation() method and defines * a custom implementation. * * @param value Object to validate. * @return Array containing a ValidationResult object for each error. */ override protected function doValidation(value:Object):Array { // Convert value to a String. var inputValue:String = StringUtil.trim(String(value)); // Clear the results array. _results = []; // Call base class doValidation(). _results = super.doValidation(inputValue); // Return if there are errors. if (_results.length > 0) return _results; // If the string is empty, return. if (inputValue.length == 0) return _results; // Get the filename's extension. var fileExtension:String = inputValue.substring(inputValue.lastIndexOf(".")).toLowerCase(); // If file extension isn't in the array, add an error. if (_validExtensions.indexOf(fileExtension) == -1) { _results.push(new ValidationResult(true, null, ERROR_BAD_EXTENSION, _errorMessage)); return _results; } return _results; } } }
To override the superclass's definition of the doValidation() method, simply prefix the method definition with the keyword override. Next, write the new definition of the doValidation() method, following these steps:
 
  1. Trim the string value to get rid of any leading or trailing spaces.
  2. Clear the _results array.
  3. Call the superclass's doValidation() method and store any validation errors.
  4. Check the _results array's length; a value greater than 0 indicates errors from the previous step's validation call. You'll want to return the array immediately, skipping the rest of the method.
  5. Test the string's length. If it is zero, return.
  6. Extract the file extension. Note that for this project, you're getting all the characters from the last dot (.) in the string to the end of the filename; thus, if the filename is myFile.jpg, the function returns .jpg for the extension.
  7. Use the handy indexOf() method on the _validExtensions array to compare it to the file's extension. If it returns -1, then the extension was not found.
  8. To indicate a validation error, add a new ValidationResult object to the _results array using the push method. The following comprise the arguments passed to the ValidationResult constructor:
     
    1. true: Boolean value indicating if this is an error;
    2. null: String name of any subfield with which the result is associated;
    3. ERROR_BAD_EXTENSION: String error code (in this case, just an arbitrary string containing BadFileExtension);
    4. _errorMessage: String the user sees when hovering over the field with the error.
  9. Finally, return the _results array.
The above steps can be modified to meet your validation criteria. This particular validator class was for a Flex app handling image uploads, so file extension validation was one of the requirements.
 

 
Using the custom validator in your application

In addition to controlling layout, the Flex Form container lets you handle data binding and error messages. In this way, it provides greater power and functionality than its HTML counterpart.
 
To use the newly-created FileExtensionValidator class, switch to the CustomValidator.mxml file that was automatically created by Flex Builder when you started this project. If it's not already open, follow these steps to open it:
 
  1. Choose File > Open File
  2. Navigate to the root folder of your project.
  3. Open the src folder.
  4. Select the file CustomValidator.mxml.
  5. Click Open.
You use the <mx:Form> tag to define a Flex Form. Nested within the Form control, the FormHeading component provides a convenient way to label a group of FormItem containers. The FormItem provides a label and one or more child elements, arranged horizontally or vertically.
 
Now add a Form container and a few controls to the CustomValidator.mxml file:
 
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="/2006/mxml" layout="absolute"> <mx:Form id="myForm"> <mx:FormHeading label="Flex Custom Validator" /> <mx:FormItem label="Provide an image filename" required="true"> <mx:TextInput id="fileName" /> </mx:FormItem> <mx:FormItem> <mx:Button label="Validate Me!" click="validateForm()" /> </mx:FormItem> </mx:Form> </mx:Application>
The above code defines a Form container with two nested FormItem elements. The first FormItem holds a TextInput while the second contains a Button. The required attribute on the TextInput FormItem tells Flex to (what else) require a value in that field. In addition, it puts a red asterisk next to the field, one of the standard ways to indicate required items. To see what this code generates, you can load it in your browser by choosing Run > Run CustomValidator (see Figure 3).
 
Figure 3. Custom Validator App
Figure 3. Custom Validator App
You can now add the custom validator to your form:
 
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="/2006/mxml" layout="absolute" xmlns:myutil="MyUtilities.*"> <mx:Script> <![CDATA[ // Explicitly call the validate method on the // custom validator object. privatefunction validateForm():void { fileNameValidator.validate(); } ]]> </mx:Script> <myutil:FileExtensionValidator id="fileNameValidator" errorMessage="You can only upload JPG, JPEG, GIF, and PNG files." validExtensions="{new Array('.jpg','.jpeg','.gif','.png')}" source="{fileName}" property="text" required="true" /> <mx:Form id="myForm"> <mx:FormHeading label="Flex Custom Validator" /> <mx:FormItem label="Provide an image filename" required="true"> <mx:TextInput id="fileName" /> </mx:FormItem> <mx:FormItem> <mx:Button label="Validate Me!" click="validateForm()" /> </mx:FormItem> </mx:Form> </mx:Application>
You reference the new validator class by declaring a namespace on the Application tag and point it to MyUtilities.*. Your MXML code uses the myutil prefix to access code in that namespace, which contains only one element – FileExtensionValidator.
 
When you press the space key from within the custom tag, Flex Builder will automatically show all the public properties and methods of your custom validator, just as it does for all the built-in validators (see Figure 4). Note the custom errorMessage property in the dropdown; the other custom property, validExtensions, though not visible in the figure, is a little further down the same dropdown.
 
Figure 4. Auto-complete for the Custom Validator
Figure 4. Auto-complete for the Custom Validator
Also note the curly braces for the Array instantiation; this is referred to as property binding. Anything within the curly braces will become a function at compile time; without the braces, Flex will consider the value to be simply a string passed to the custom validator.
 
To validate the fileName field, set the source attribute of the custom validator to point to it (again, within curly braces). Continuing on with the tag, the property attribute tells the validator which property on the fileName field it needs to check (the text property). The required property is set to true by default; however, you can also set it here for clarity.
 
The validation occurs automatically when the fileName field loses focus. You can also add a Button control and explicitly call the validate() method on the FileNameValidator object. The click event of the Button control calls the validateForm() function, which in turn validates the form.
 
With all the pieces in place, you can run the code to see what it generates. Choose Run > Run CustomValidator. Try typing in a good value (such as abc.jpg) and clicking the "Validate Me!" button; then try a bad value (such as abc.htm). Flex highlights the field with red to indicate an error; rolling over the field will display the following message (see Figure 5): "You can only upload JPG, JPEG, GIF, and PNG files." If you leave the field blank, the custom validator displays the following error from the base class, or superclass, from which it was extended: "This field is required."
 
Figure 5. Custom Validator with Error
Figure 5. Custom Validator with Error
You can modify the custom validator to meet your business needs, employing more complex validation. For example, if you're processing the user's name, your custom validator could validate the first, middle, and last names – all at once. To do this, bind the fields to an <mx:Model> object containing elements for each field; then set the validator's source attribute to the model. The doValidation() method will receive a value object as its argument, and you can simply access the fields as properties on the value object (such as value.firstName). If any of the fields do not validate, add a new ValidationResult object to the results array. In this case, instead of passing null to the second (subfield) parameter of the ValidationResult constructor, use the ID of the field in question, for example:
 
ValidationResult(true, "firstName", ERROR_BAD_FIRSTNAME, errorMessage)

 
Where to go from here

Creating a custom validator in Flex reveals the power of its RIA framework: You can extend the built-in components and add additional functionality to meet your project's requirements, all the while following good programming practices. Input validation, one of the core principles of secure web application development, also serves to protect you from storing bad information in your database. Nonetheless, it is a best practice to always validate on the server as well.
 
So how do you build a custom validator based on your business needs? Start with some simple questions:
 
  • What inputs do you take from your users?
  • Does the input require a specific type, such as numeric only?
  • Does the input need to meet a specific format, such as a date?
  • Are any fields required?
To learn more about the Flex validation controls, visit Flex Quick Start: Handling Data.
 
To learn and use the ASDoc command-line tool, visit the "Using ASDoc" section of the Flex 3 Help.