tutorials flex validators

Validation in Flex (Validators)

Article written the 27/07/2007 10:30.
By iteratif ( Bugalotto Olivier ).

The development of a data-entry form requires the creation of some sort of validation of the data entered by users to guarantee security. This can sometimes be long and tiresome.

Here is an example of such a validation:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" 
	width="380" height="250" verticalAlign="middle">
 
	<mx:Script>
		<![CDATA[
			import mx.controls.Alert;
			private function onConnection():void {
				var email:String = txtEmail.text;
				var pass:String = txtPass.text;
 
				var isEmail:Boolean = validateEmail(email);
				var isNoEmpty:Boolean = pass.length != 0;
 
				if(isEmail && isNoEmpty) {
					frmItemEmail.required = frmItemPass.required = false;
					Alert.show("Validation acheived!","Information");
			} else {
					frmItemEmail.required = frmItemPass.required = true;
					Alert.show("The fields with a * are obligatory.",
							   "Information");
				}
			}
 
			private function validateEmail(email:String):Boolean {
				var pattern:RegExp = /(\w|[_.\-])+@((\w|-)+\.)+\w{2,4}+/;
				var result:Object = pattern.exec(email);
				if(!result)
					return false;
				return true;
			}
		]]>
	</mx:Script>
 
	<mx:Panel width="316" height="172" layout="absolute" title="Connection">
		<mx:Form right="10" left="10" top="10" bottom="10">
			<mx:FormItem id="frmItemEmail" label="Email" width="100%">
				<mx:TextInput id="txtEmail" width="100%"/>
			</mx:FormItem>
			<mx:FormItem id="frmItemPass" label="Password" width="100%">
				<mx:TextInput id="txtPass" width="100%" displayAsPassword="true"/>
			</mx:FormItem>
		</mx:Form>
		<mx:ControlBar height="42" y="128" horizontalAlign="right">
			<mx:Button label="Connection" click="onConnection()"/>
		</mx:ControlBar>
	</mx:Panel>
 
</mx:Application>

As you can see the validation process is very important for 2 textfields. Imagine the validation required for a more complicated form such as a user subscription.

The Flex2 framework proposes an ensemble of non-visual components which allow us to validate the data. These components are called ‘validators’. There are currently many of these.

Here is our previous example after using a validator:

        <mx:EmailValidator id="validEmail" source="{txtEmail}" property="text" />
	<mx:StringValidator id="validPass" source="{txtPass}" property="text" />
 
	<mx:Panel width="316" height="172" layout="absolute" title="Connection">
		<mx:Form right="10" left="10" top="10" bottom="10">
			<mx:FormItem label="Email" width="100%">
				<mx:TextInput id="txtEmail" width="100%"/>
			</mx:FormItem>
			<mx:FormItem label="Password" width="100%">
				<mx:TextInput id="txtPass" width="100%" displayAsPassword="true"/>
			</mx:FormItem>
		</mx:Form>
		<mx:ControlBar height="42" y="128" horizontalAlign="right">
			<mx:Button id="btnConnect" label="Connection" click="onConnection()" />
		</mx:ControlBar>
	</mx:Panel>

The big problem with this example is that it doesn’t render the connection button unclickable. Validators launch validation while losing the focus of the validation component.

Note: this is not on the focusOut but on the valueCommit that produces the validation.

We need to change this behaviour and action the validation on the click of the button. To do this we just need to modify the triggerEvent property and also the trigger property of the validator:

       <mx:EmailValidator id="validEmail" source="{txtEmail}" property="text"
		trigger="{btnConnect}" triggerEvent="click" />
	<mx:StringValidator id="validPass" source="{txtPass}" property="text" 
		trigger="{btnConnect}" triggerEvent="click" />

This doesn’t fix the problem, we have the validation when clicking the button but it doesn’t stop the alert window from displaying. To fix this problem you need to execute the validate() method in the validator which will return an event that we can test:

private function onConnection():void {
	var email:String = txtEmail.text;
	var pass:String = txtPass.text;	
 
	var reValidEmailEvent:ValidationResultEvent = validEmail.validate();
	var reValidPassEvent:ValidationResultEvent = validPass.validate();
 
	// We test the event returned by the validate() method
	if(reValidEmailEvent.type == ValidationResultEvent.VALID
		  && reValidPassEvent.type == ValidationResultEvent.VALID) {
		// The StringUtil.substitute method allows you to use indices
		// to substitute with the parameters:
		// here {0} will be substituted with the value of the ‘email’ variable
		var msg:String = StringUtil.substitute("Welcome\nYour email is {0}",email);
		Alert.show(msg,"Welcome");  	
	}				
}

The validation logic is still cumbersome in a long form. It would be better to be able to validate everything once only. This is possible with the Validator class; the basic class for predefined validators.

This class proposes the static but very useful method validateAll() which validates a validator table and sends back an empty table if the validation is achieved:

private function onConnection():void {
	var email:String = txtEmail.text;
	var pass:String = txtPass.text;	
 
	var validators:Array = Validator.validateAll([validEmail,validPass]);
 
	// If the table contains at least one element
	// we get an error message
	if(!validators.length) {
		// The StringUtil.substitute method allows you to use indices
		// substitute with the parameters:
		// here {0} will be substituted with the value of the ‘email’ variable
		var msg:String = StringUtil.substitute("Welcome\nYour email is {0}",email);
		Alert.show(msg," Welcome ");  	
	}				
}

If the table contains at least one element this is a ValidationResultEvent type element.

We can also clean up the error messages by modifying the errorString property of components to validate:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" 
	verticalAlign="middle" width="450" height="250" horizontalAlign="left" 
	backgroundGradientColors="[#8080ff, #c0c0c0]">
 
	<mx:Script>
		<![CDATA[
			import mx.validators.Validator;
			import mx.utils.StringUtil;
			import mx.controls.Alert;
			import mx.events.ValidationResultEvent;
 
			private function onConnection():void {
				var email:String = txtEmail.text;
				var pass:String = txtPass.text;	
 
				var validators:Array = Validator.validateAll([validEmail,validPass]);
 
				if(!validators.length) {
					var msg:String = StringUtil.substitute(
							"Welcome\nYour email is {0}",email);
					Alert.show(msg,"Welcome");  	
				}				
			}
 
			private function onResetForm():void {
				// Deletion of user name captured
				txtEmail.text = "";
				// Deletion of error message
				txtEmail.errorString = "";
 
				txtPass.text = "";
				txtPass.errorString = "";
			}
		]]>
	</mx:Script>
 
	<mx:EmailValidator id="validEmail" source="{txtEmail}" property="text"
		trigger="{btnConnect}" triggerEvent="click" 
		requiredFieldError="This textfield is obligatory."
		missingAtSignError="The character @ is missing in the email address."
		missingPeriodInDomainError="The domain is missing from the email address."
		missingUsernameError="The user name is missing from your email address." />
	<mx:StringValidator id="validPass" source="{txtPass}" property="text" 
		trigger="{btnConnect}" triggerEvent="click"
		requiredFieldError=" This textfield is obligatory." />
 
	<mx:Panel width="316" height="172" layout="absolute" title="Connection" 
		titleIcon="@Embed('assets/network.png')">
		<mx:Form right="10" left="10" top="10" bottom="10">
			<mx:FormItem label="Email" width="100%">
				<mx:TextInput id="txtEmail" width="100%"/>
			</mx:FormItem>
			<mx:FormItem label="Password" width="100%">
				<mx:TextInput id="txtPass" width="100%" displayAsPassword="true"/>
			</mx:FormItem>
		</mx:Form>
		<mx:ControlBar height="42" y="128" horizontalAlign="right">
			<mx:Button label="Reset" click="onResetForm()"/>
			<mx:Button id="btnConnect" label="Connection" click="onConnection()" />
		</mx:ControlBar>
	</mx:Panel>
 
</mx:Application>

VALID and INVALID events

We can manage the validation by listening to the valid and invalid events broadcast by the validation component or by the component itself.

In this example we listen to the two events in the validation component to choose whether or not to activate the save button:

<?xml version=“1.0” encoding=“utf-8”?> <mx:Application xmlns:mx=“http://www.adobe.com/2006/mxml” layout=“vertical”

verticalAlign="middle">

<mx:Script>
	<![CDATA[
		import mx.controls.Alert;
		import mx.utils.StringUtil;
		private function onValid():void {
			btnSave.enabled = true;
		}
		
		private function onInvalid():void {
			btnSave.enabled = false;	
		}
		
		private function onSave():void {
			var email:String = txtEmail.text;
			var surname:String = txtSurname.text ? txtSurname.text : "";
			var name:String = txtFirst name.text ? txtName.text : "";
			
			var msg:String = StringUtil.substitute("Email: {0}\nSurname: " + 
					"{1}\nName: {2}",email,surname,name);
													
			Alert.show(msg,"New contact");
		}
	]]>
</mx:Script>

<mx:EmailValidator source="{txtEmail}" property="text"
	valid="onValid()" invalid="onInvalid()" />

<mx:Panel title="Contact" width="284" height="202" layout="absolute" 
	titleIcon="@Embed('assets/addressbook2.png')">
	<mx:Form right="10" left="10" top="10" bottom="10">
		<mx:FormItem label="Email" width="100%" required="true">
			<mx:TextInput id="txtEmail" width="100%"/>
		</mx:FormItem>
		<mx:FormItem label="Surname" width="100%">
			<mx:TextInput id="txtSurname" width="100%"/>
		</mx:FormItem>
		<mx:FormItem label="Pseudo" width="100%">
			<mx:TextInput id="txtName" width="100%"/>
		</mx:FormItem>
	</mx:Form>
	<mx:ControlBar height="41" y="143" horizontalAlign="right">
		<mx:Button id="btnSave" label="Save" enabled="false"
			click="onSave()"/>
	</mx:ControlBar>
</mx:Panel>

</mx:Application> </code>

Personalised error messages

We are going to look at how to personalise error messages that until now were defined by default (and always in English).

The basic Validator class possesses the requiredFieldError property which allows you to modify error messages.

	<mx:EmailValidator id="validEmail" source="{txtEmail}" property="text"
		trigger="{btnConnect}" triggerEvent="click" 
		requiredFieldError="This textfield is obligatory." />
	<mx:StringValidator id="validPass" source="{txtPass}" property="text" 
		trigger="{btnConnect}" triggerEvent="click"
		requiredFieldError="This textfield is obligatory." />

But each validator proposes different error messages like in this case with EmailValidator :

	<mx:EmailValidator id="validEmail" source="{txtEmail}" property="text"
		trigger="{btnConnect}" triggerEvent="click" 
		requiredFieldError="This textfield is obligatory."
		missingAtSignError="The character @ is missing in the email address."
		missingPeriodInDomainError="The domain is missing in your email address."
		missingUsernameError="The user name is missing in your email address." />
	<mx:StringValidator id="validPass" source="{txtPass}" property="text" 
		trigger="{btnConnect}" triggerEvent="click"
		requiredFieldError="This textfield is obligatory." />

We can also personalise the type of error window. To do this you need to modifiy the .errorTip style defined by default in the default.css stylesheet that you will find in the Flex SDK 2 directory. In our example, we are just going to modify the colour of the window.

	<mx:Style>
		.errorTip {
			borderColor: #8080ff;
		}
	</mx:Style>

Creation of a validation component

To build a new validation component you need to create a new class which will inherit the Validator and redefine the doValidation() method. It is this method that is responsible for the validation.

We are going to create a validation component which will compare two textfields as is often the case when we confirm a password.

package {
	import mx.validators.Validator;
	import mx.utils.ObjectUtil;
	import mx.validators.ValidationResult;
	import mx.utils.StringUtil;
 
	public class CompareStringValidator extends Validator {
		private var results:Array;
 
		public var valueCompare:Object;
 
		public function CompareStringValidator() {
			super();
		}
 
		protected override function doValidation(value:Object):Array {
			// Initialise the error table
            results = [];
 
            results = super.doValidation(value);        
 
			// If the table contains at least one element
			// we return it
            if (results.length > 0)
                return results;
 
 
			var strValue:String = String(value);
			var compareStrValue:String = String(valueCompare);
 
			// If it is not equal to stringCompare we return 0
			var result:int = ObjectUtil.stringCompare(strValue,compareStrValue);
			if(result) {
				var errorMessage:String = 
					StringUtil.substitute("The value {0} is not equal to the " +
 								"value {1}",strValue,compareStrValue);
					// Her we build a ValidationResult event
					// to add to the table
					results.push(new ValidationResult(true,null,
					      "Invalid comparison",errorMessage));
			}
 
			// We return the table
			return results;
		}
	}
}

And here is its use in your application:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" xmlns="*">
 
        <!—the attribute (property) sourceCompare is the comparison value -->
	<CompareStringValidator source="{txtComparePass}" property="text" sourceCompare="{txtPass.text}" />
 
	<mx:Panel width="340" height="207" layout="absolute" title="Inscription">
		<mx:Form right="10" left="10" top="10" bottom="10">
			<mx:FormItem label="Email" width="100%">
				<mx:TextInput width="100%"/>
			</mx:FormItem>
			<mx:FormItem label="Password" width="100%">
				<mx:TextInput id="txtPass" width="100%" displayAsPassword="true"/>
			</mx:FormItem>
			<mx:FormItem label="Confirm password" width="100%">
				<mx:TextInput id="txtComparePass" width="100%" displayAsPassword="true"/>
			</mx:FormItem>
		</mx:Form>
		<mx:ControlBar horizontalAlign="right">
			<mx:Button label="Save"/>
		</mx:ControlBar>
	</mx:Panel>
 
</mx:Application>

The validation components add a certain comfort but we feel that there is still some work to be done compared to the validation that we can find in other languages.

You can find the source code to this tutorial here: Validators.zip

By ITERATIF - BUGALOTTO Olivier (2007)








Adobe Training center


Mediabox Training Centre © 2000 - 2008 All rights reserved.
Adobe Authorized Training Centre. State convention under number 25 14 02167 14.
Mediabox : SARL au capital de 62.000€ - Activity number: 25 14 02167 14 - SIRET : 493 716 468 00027
MEDIABOX, 102 Avenue des Champs Elysées, 75008 PARIS - Tel. +33(0)2.31.91.96.89 - Fax. +33(0)2.72.68.56.42