Introduction
Flex 3 has nicely build-in validation mechanism, but sometimes you wish they had not embedded this so deep into the components, that customization becomes a laborish task. I have also read many articles on customizations of this mechanism, but I have not found a working solution to my specific wish. What I want is validation of single input fields, multiple input and arbitrary placement of errors
besides the tooltip method.
Requirements
- Flex Builder 3
- Tutorial code (See step 7 for both files)
Steps
Now I am going to describe the steps I took to create this validator.
Step 1
I have created a Flex (web) project in my workspace. In this project I have 1 mxml file (my application file). I have named this ValidationTutorial.mxml.
1 2 3 | <?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" xmlns:local="*"> </mx:Application> |
In this mxml I will put 3 input field (day, month, year) in a hbox. Furthermore I will place a hidden Label
below the hbox. These are my basic components that I will be using to demonstrate the multiple input validator.
1 2 3 4 5 6 7 8 9 | <?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" xmlns:local="*"> <mx:HBox> <mx:TextInput id="day" /> <mx:TextInput id="month" /> <mx:TextInput id="year" /> </mx:HBox> <mx:Label id="errorMsgbirthdayValidationModel" visible="false" includeInLayout="false"/> </mx:Application> |
Step 2
Code our custom validator. Now we have to create the validator. file -> new -> actionscript class and call it BirthdayValidator (make it extend Validator). override the protected function doValidation, put a call to super.doValidation(value) in it and we have our foundation for the validator in place. Your actionscript class should look as follows
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | package { public class BirthdayValidator extends Validator { public function BirthdayValidator() { super(); } override protected function doValidation(value:Object):Array { // Init the results array with the basic validation var results:Array = super.doValidation(value); return results; } } } |
Step 3
This is a relatively small step, but contains the magic that will enable me to validate and control the output regarding 3 input fields. Make a mental note of the valError element, which I will use later on.
1 2 3 4 5 6 7 8 9 10 | <mx:Model id="birthdayValidationModel"> <date> <birthday> <day>{day.text}</day> <month>{month.text}</month> <year>{year.text}</year> <valError></valError> </birthday> </date> </mx:Model> |
Step 4
We define the validator and link all components together. this means we are going to tell the validator that the source for validation is our model and the results will be displayed in the label. If you now run the application, nothing will happen when tabbing across the fields. This is because we have not defined the triggers for the validator and the actual implementation of the validator. We will put this on the last input with focusOut. The implementation for the validator is for tutorial purpose simple.
1 2 3 4 5 6 7 8 | <local:BirthdayValidator id="birthdayVal" required="true" source="{birthdayValidationModel}" property="birthday" listener="{errorMsgbirthdayValidationModel}" trigger="{year}" triggerEvent="focusOut"/> |
We are now close to a working example.
Step 5
Now we define the invalid handler and the valid handler. These methods will contain code to set/unset validation texts. Configure them into the validator and edit the label so that it responds to the errors in it. When you have done this, you will see validation results below the inputs when you enter no values and focusOut on the year input (last input field).
1 2 3 4 5 6 7 8 9 10 | <local:BirthdayValidator id="birthdayVal" required="true" source="{birthdayValidationModel}" property="birthday" listener="{errorMsgbirthdayValidationModel}" trigger="{year}" triggerEvent="focusOut" invalid="onInvalidHandler(event)" valid="onValidHandler(event)"/> |
And the methods….
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | /** * Validationresult event handler. This function * places the validation results into the appropriate * label for error text placement beneath an input * text. * * @param event Results of the input box(es) validation */ public static function onInvalidHandler( event:ValidationResultEvent ):void { var _error:String = ""; var validator:Validator = event.target as Validator; for each ( var result:ValidationResult in event.results ) { _error+=result.errorCode; } // All validation results will be displayed in a label component var errorLabel:Label = validator.listener as Label; errorLabel.text = _error; event.stopImmediatePropagation(); } /** * Validation result handler. This function removes all * error texts from the error label for the current input * component. * * @param event Results of the input box(es) validation */ public static function onValidHandler( event:ValidationResultEvent ):void { var _error:String = ""; var validator:Validator = event.target as Validator; // All validation results will be displayed in a label component var errorLabel:Label = validator.listener as Label; errorLabel.text = _error; event.stopImmediatePropagation(); } |
Step 6
Add styling. Just some nice sugar, but styling is everything. Add this styling to your ValidationTutorial.mxml
Add to the 3 input field the following property
1 | styleName="{ValidatorUtil.showErrorBorder((birthdayValidationModel.birthday.valError as XML), this.day)}" |
Step 7
Now comes the 4th element in the mx:model into focus. When you run the application you will see deep blue borders, but they do not change to red borders were there is an error detected by the validation. For this to work we need to programaticly fill the error element to reflect the inputs that are invalid.
This we will do in the validator and a helper method in our ValidationTutorial.mxml
The BirthdayValidator
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | package { import mx.validators.ValidationResult; import mx.validators.Validator; public class BirthdayValidator extends Validator { private static const ERROR_START_ELEMENT_TAG:String = "<errorElement>"; private static const ERROR_END_ELEMENT_TAG:String = "</errorElement>"; private static const ERROR_DAY_TAG:String = "<error>day</error>"; private static const ERROR_MONTH_TAG:String = "<error>month</error>"; private static const ERROR_YEAR_TAG:String = "<error>year</error>"; public function BirthdayValidator() { super(); } override protected function doValidation(value:Object):Array { var day:String = value.day; var month:String = value.month; var year:String = value.year; var errorString:String = ERROR_START_ELEMENT_TAG; // Init the results array with the basic validation var results:Array = super.doValidation(value); if (day == "" || day == null) { // Texts and codes need to be refactered out of this util // into a text file results.push(new ValidationResult(true, "day", "noDay", " No day")); errorString += ERROR_DAY_TAG; } // Check month field. if (month == "" || month == null) { // Texts and codes need to be refactered out of this util // into a text file results.push(new ValidationResult(true, "month", "noMonth", " No month")); errorString += ERROR_MONTH_TAG; } // Check month field. if (year == "" || year == null) { // Texts and codes need to be refactered out of this util // into a text file results.push(new ValidationResult(true, "year", "noYear", " No year")); errorString += ERROR_YEAR_TAG; } errorString += ERROR_END_ELEMENT_TAG; value.valError = new XML(errorString); return results; } } } |
The ValidatorTutorial.mxml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | <?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" xmlns:local="*"> <mx:Style> .errorBorderStyle { border-color: red; border-style: solid; border-thickness: 2px; border-thickness-bottom: 2px; border-thickness-left: 2px; border-thickness-right: 2px; border-thickness-top: 2px; } .defaultBorderStyle { border-color: blue; border-style: solid; border-thickness: 2px; border-thickness-bottom: 2px; border-thickness-left: 2px; border-thickness-right: 2px; border-thickness-top: 2px; } </mx:Style> <mx:Script> <![CDATA[ import mx.core.UIComponent; import mx.validators.ValidationResult; import mx.validators.Validator; import mx.events.ValidationResultEvent; /** * Validationresult event handler. This function * places the validation results into the appropriate * label for error text placement beneath an input * text. * * @param event Results of the input box(es) validation */ public static function onInvalidHandler( event:ValidationResultEvent ):void { var _error:String = ""; var validator:Validator = event.target as Validator; for each ( var result:ValidationResult in event.results ) { _error+=result.errorCode; } // All validation results will be displayed in a label component var errorLabel:Label = validator.listener as Label; errorLabel.text = _error; event.stopImmediatePropagation(); } /** * Validation result handler. This function removes all * error texts from the error label for the current input * component. * * @param event Results of the input box(es) validation */ public static function onValidHandler( event:ValidationResultEvent ):void { var _error:String = ""; var validator:Validator = event.target as Validator; // All validation results will be displayed in a label component var errorLabel:Label = validator.listener as Label; errorLabel.text = _error; event.stopImmediatePropagation(); } /** * Function reserved for complex objects. In this we mean mx:model * type objects for multi input validations. (dates where day,month,year * are seperated input controls). * * @param errors A xmllist of elements containing the controls that need * or do not need error styling. * @param input the control we like to style. * @return the new styling information. */ public static function showErrorBorder( errors:XML, input:UIComponent ):String { var styleName:String = "defaultBorderStyle"; // Check for not null, otherwise the flashplayer // will silently ignore the nullpointer and break off // any further styling for the target control if ( errors != null && errors.error != null ) { for each ( var error:XML in errors.error ) { if ( error == input.id ) { styleName = "errorBorderStyle"; break; } } } return styleName; } ]]> </mx:Script> <mx:Model id="birthdayValidationModel"> <date> <birthday> <day>{day.text}</day> <month>{month.text}</month> <year>{year.text}</year> <valError></valError> </birthday> </date> </mx:Model> <mx:HBox> <mx:TextInput id="day" styleName="{showErrorBorder((birthdayValidationModel.birthday.valError as XML), this.day)}"/> <mx:TextInput id="month" styleName="{showErrorBorder((birthdayValidationModel.birthday.valError as XML), this.month)}"/> <mx:TextInput id="year" styleName="{showErrorBorder((birthdayValidationModel.birthday.valError as XML), this.year)}"/> </mx:HBox> <mx:Label id="errorMsgbirthdayValidationModel" visible="{errorMsgbirthdayValidationModel.text!=''}" includeInLayout="{errorMsgbirthdayValidationModel.text!=''}"/> <local:BirthdayValidator id="birthdayVal" required="true" source="{birthdayValidationModel}" property="birthday" listener="{errorMsgbirthdayValidationModel}" trigger="{year}" triggerEvent="focusOut" invalid="onInvalidHandler(event)" valid="onValidHandler(event)"/> </mx:Application> |
Final note
And now the magic is complete. You have build a multiple input validator, with error message customization and placement. You can add many enhancement, that I will leave up to your imagination.
I hope you enjoy coding Flex/ActionScript as I do.
Kind regards,
Tags: Actionscript, custom, Flex 3, input, Validation, validator
Nice article
Good stuff, your effort nicely complements Flex 3 livedocs sample
“Example: Validating multiple fields”
http://livedocs.adobe.com/flex/3/html/help.html?content=createvalidators_4.html