I want to use a validator to ensure 2 password fields match in Flex. I want the validator to highlight form fields like a normal flex validation control. Thanks.
Unless you have to use a validator, you can simply attach a change event handler and set errorString
property of the password field when the passwords don't match. It gives the same highlighting as done by validator.
enter code here
I created my own custom validator (mostly copied from date validator):
package validators
{
import mx.validators.ValidationResult;
import mx.validators.Validator;
public class PasswordValidator extends Validator
{
// Define Array for the return value of doValidation().
private var results:Array;
public function PasswordValidator()
{
super();
}
public var confirmationSource: Object;
public var confirmationProperty: String;
// Define the doValidation() method.
override protected function doValidation(value:Object):Array {
// Call base class doValidation().
var results:Array = super.doValidation(value.password);
if (value.password != value.confirmation) {
results.push(new ValidationResult(true, null, "Mismatch",
"Password Dosen't match Retype!"));
}
return results;
}
/**
* @private
* Grabs the data for the confirmation password from its different sources
* if its there and bundles it to be processed by the doValidation routine.
*/
override protected function getValueFromSource():Object
{
var value:Object = {};
value.password = super.getValueFromSource();
if (confirmationSource && confirmationProperty)
{
value.confirmation = confirmationSource[confirmationProperty];
}
return value;
}
}
}
the example mxml for using:
<validators:PasswordValidator id="pwvPasswords" required="true" source="{txtPassword}" property="text" confirmationSource="{txtPasswordConfirm}" confirmationProperty="text" trigger="{btnStep2Finish}" />
It's pretty basic, but it's mostly what I need. It only highlights the password box though, would like to get it to highlight both.
Jack,
if you want highlight both password boxes couldn't you just duplicate your Passwordvalidator and switch source and confirmationSource.
I've done this in a different way using custom validation rules.
Check it out: http://martypitt.wordpress.com/2009/08/26/rule-based-asynchronous-validation-in-flex-forms/
It shows how to do async validation (eg., checking a username is available on the server), and other validation scenario's that don't fit neatly into flex's out-of-the-box framework, including two passwords match.
Eg:
<mx:Button label="Register" enabled="{ validationRules.isValid }" />
<validation:ValidationRuleCollection id="validationRules">
<validation:UsernameIsUniqueValidationRule username="{ txtUsername.text }" targetComponent="{ txtUsername }" />
<validation:EmailIsUniqueValidationRule email="{ txtEmailAddress.text }" targetComponent="{ txtEmailAddress }" />
<validation:PasswordsMustMatchValidationRule password1="{ txtPassword.text }" password2="{ txtPasswordConfirm.text }" targetComponent="{ txtPasswordConfirm }" />
<mx:StringValidator required="true" source="{ txtUsername }" property="text" requiredFieldError="{ ResourceManager.getInstance().getString( ResourceBundles.ERROR_MESSAGES , 'REQUIRED_FIELD_ERROR' )}" />
</validation:ValidationRuleCollection>
Here's a better custom validator that's more universal, clean, and works well with the 'required' field.
import mx.validators.ValidationResult;
import mx.validators.Validator;
public class MatchValidator extends Validator{
private var _matchSource: Object = null;
private var _matchProperty: String = null;
private var _noMatchError: String = "Fields did not match";
[Inspectable(category="General", defaultValue="Fields did not match")]
public function set noMatchError( argError:String):void{
_noMatchError = argError;
}
public function get noMatchError():String{
return _noMatchError;
}
[Inspectable(category="General", defaultValue="null")]
public function set matchSource( argObject:Object):void{
_matchSource = argObject;
}
public function get matchSource():Object{
return _matchSource;
}
[Inspectable(category="General", defaultValue="null")]
public function set matchProperty( argProperty:String):void{
_matchProperty = argProperty;
}
public function get matchProperty():String{
return _matchProperty;
}
override protected function doValidation(value:Object):Array {
// Call base class doValidation().
var results:Array = super.doValidation(value.ours);
var val:String = value.ours ? String(value.ours) : "";
if (results.length > 0 || ((val.length == 0) && !required)){
return results;
}else{
if(val != value.toMatch){
results.length = 0;
results.push( new ValidationResult(true,null,"mismatch",_noMatchError));
return results;
}else{
return results;
}
}
}
override protected function getValueFromSource():Object {
var value:Object = {};
value.ours = super.getValueFromSource();
if (_matchSource && _matchProperty){
value.toMatch = _matchSource[_matchProperty];
}else{
value.toMatch = null;
}
return value;
}
}
Here's an example:
<components:MatchValidator source="{passwordCheckField}" property="text" matchSource="{passwordField}" matchProperty="text"
valid="passwordsMatch = true" invalid="passwordsMatch = false" noMatchError="Passwords do not match"/>
I expanded on Daniel's solution, adding the ability to trigger off of the matched source as well.
import flash.events.Event;
import mx.validators.ValidationResult;
import mx.validators.Validator;
public class MatchValidator extends Validator
{
private var _matchSource: Object = null;
private var _matchProperty: String = null;
private var _noMatchError: String = "Fields did not match";
[Inspectable(category="General", defaultValue="Fields did not match")]
public function set noMatchError( argError:String):void{
_noMatchError = argError;
}
public function get noMatchError():String{
return _noMatchError;
}
[Inspectable(category="General", defaultValue="null")]
public function set matchSource( argObject:Object):void{
removeTriggerHandler();
_matchSource = argObject;
addTriggerHandler();
}
public function get matchSource():Object{
return _matchSource;
}
[Inspectable(category="General", defaultValue="null")]
public function set matchProperty( argProperty:String):void{
_matchProperty = argProperty;
}
public function get matchProperty():String{
return _matchProperty;
}
override protected function doValidation(value:Object):Array {
// Call base class doValidation().
var results:Array = super.doValidation(value.ours);
var val:String = value.ours ? String(value.ours) : "";
if (results.length > 0 || ((val.length == 0) && !required)){
return results;
}else{
if(val != value.toMatch){
results.length = 0;
results.push( new ValidationResult(true,null,"mismatch",_noMatchError));
return results;
}else{
return results;
}
}
}
override protected function getValueFromSource():Object {
var value:Object = {};
value.ours = super.getValueFromSource();
if (_matchSource && _matchProperty){
value.toMatch = _matchSource[_matchProperty];
}else{
value.toMatch = null;
}
return value;
}
override public function set triggerEvent(value:String):void
{
removeTriggerHandler();
super.triggerEvent = value;
addTriggerHandler();
}
private function addTriggerHandler():void
{
if (_matchSource)
_matchSource.addEventListener(triggerEvent,triggerHandler);
}
private function removeTriggerHandler():void
{
if (_matchSource)
_matchSource.removeEventListener(triggerEvent,triggerHandler);
}
private function triggerHandler(event:Event):void
{
validate();
}
}