Apache Adobe Flex TutorialTutoriaux Adobe Flex & AIR en Français

18mai/0812

Flex Validator: Coder ses propres validateurs en ActionScript

Si l'un des validateurs standards du framework Flex ne conviennent pas à votre besoin, vous pouvez écrire des validateurs personnalisés en ActionScript. Pour écrire un validator spécial, vous devez coder une classe ActionScript qui extend mx.validators.Validator et cette classe doit surcharger la méthode doValidation(). La méthode doValidation() est protected, a besoin d'un objet en paramètres et doit retourner un tableau. Si la validation se passe mal, cette méthode renvoie un tableau d'objets de type ValidationResult. Si elle se passe bien, elle renvoie un tableau vide.

L'exemple suivant est un validateur personnalisé qui valide une valeur basée sur un nombre minimum de mots:

 package
{
	import mx.validators.ValidationResult;
	import mx.validators.Validator;

	public class WordCountValidator extends Validator
	{
		private var _count:int;

		public function get count():int{
			return _count;
		}

		public function set count(value:int):void{
			_count = value;
		}

		public function WordCountValidator(){
			super();
			_count = -1;
		}

		override protected function doValidation(value:Object):Array{
			var results:Array = new Array();
			results = super.doValidation(value);
			if (results.length > 0){
				return results;
			}
			if (_count > -1){
				var expression:RegExp= /\b\w+\b\W*/ig;
				var matches:Array = String(value).match(expression);
				if (matches.length < _count){
					results.push(new ValidationResult(true, null, "tooFewWords", "Veuillez entrer au moins " +_count+ " mots."));
				}
			}
			return results;
		}
	}
}

Et le fichier MXML permettant d'utiliser ce validateur:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:validators="*" viewSourceURL="srcview/index.html">

    <mx:Panel title="Exemple simple de WordCountValidator (perso)" width="75%" height="75%"
        paddingTop="10" paddingLeft="10" paddingRight="10" paddingBottom="10">

        <mx:Form>
            <mx:FormItem label="Commentaires">
                <mx:TextArea id="comments" width="100%"/>
            </mx:FormItem>

            <mx:FormItem >
                <mx:Button id="myButton" label="Valider" />
            </mx:FormItem>
        </mx:Form>
    </mx:Panel>

    <validators:WordCountValidator source="{comments}" property="text" count="5"/>

</mx:Application>

Flex Source Code Download: Télécharger le code source complet de l'application

This movie requires Flash Player 11

Articles similaires

Commentaires (12) Trackbacks (0)
  1. Bonjour,
    je voulais savoir si il y a validator pour un xml. C'est à dire qu'on donne XSD et le validator donne les les erreurs dans le XML.

  2. Salut,
    Non, cela n'existe pas dans Flex de base. D'ailleurs, je n'ai jamais vu de composant qui le fasse :P . Peut-être un webservice?

    Bonne chance
    Fabien

  3. Bonjour et merci pour tous ces bons tutos.

    J'ai codé le validateur suivant me servant à regarder en base si le pseudo entré lors de l'inscription par un membre est déjà présent en base. Si oui, l'inscription ne se réalise pas. Voici mon code :

    Actionscript:
    1. package myvalidators
    2. {
    3.  
    4.     import mx.rpc.events.FaultEvent;
    5.     import mx.rpc.events.ResultEvent;
    6.     import mx.rpc.remoting.RemoteObject;
    7.     import mx.validators.ValidationResult;
    8.     import mx.validators.Validator;
    9.        
    10.     public class UniquePseudoValidator extends Validator
    11.     {      
    12.         private var _results:Array;
    13.         private var _ro:RemoteObject;
    14.         private var _retour:Boolean;
    15.         public var universObj:Object;
    16.        
    17.         public function UniquePseudoValidator()
    18.         {
    19.             super();
    20.         }
    21.        
    22.         override protected function doValidation(value:Object):Array
    23.         {         
    24.             _results = super.doValidation(value.pseudo);
    25.            
    26.             _ro = new RemoteObject();
    27.             _ro.destination = "zend";
    28.             _ro.source = "ServiceClasse";
    29.             _ro.addEventListener(ResultEvent.RESULT, result);
    30.             _ro.addEventListener(FaultEvent.FAULT, fault);
    31.             _ro.isUniquePseudo(value)
    32.            
    33.             var universCap:String = value.univers.charAt(0).toUpperCase() + value.univers.substr(1, value.univers.length - 1);
    34.            
    35.             if(_retour == false)
    36.                 _results.push(new ValidationResult(true, null, "PasUnique""Le pseudo entré existe déjà pour l'univers "+ universCap));
    37.            
    38.             return _results;
    39.         }
    40.    
    41.         private function result(event:ResultEvent):void
    42.         {
    43.             _retour = event.result as Boolean;
    44.  
    45.         }
    46.        
    47.         private function fault(event:FaultEvent):void
    48.         {
    49.             _results.push(new ValidationResult(true, null, "ErreurServeur", "Une erreur serveur est survenue lors de la validation du compte"));
    50.         }
    51.        
    52.         override protected function getValueFromSource():Object
    53.         {
    54.             var value:Object = new Object();
    55.            
    56.             value.pseudo = super.getValueFromSource();
    57.                        
    58.             value.univers = universObj.univers;
    59.             value.num = universObj.num;
    60.            
    61.             return  value;
    62.         }
    63.            
    64.     }
    65. }

    Mon problème (je pense) est que le traitement du retour de ma fonction PHP isUniquePseudo par la fonction result s'effectue postérieurement au test du if dans ma fonction doValidation. En conséquence, je rentre toujours dans mon if et mon validateur me dit toujours que mon pseudo existe déjà...

    Quelle méthode adopter afin de casser le mode asynchrone de mon remoteObject afin de traiter les instructions de façon linéaire ? Je suis quasiment sur que le problème vient de là mais sait on jamais peut être qu'il y a quelque chose qui m'a échappé...

    Merci et merci encore pour les tutos !

  4. Salut Jlauney,
    effectivement, ton appel est asynchrone et tu testes trop tôt ta valeur. Pourquoi ne pas faire ce traitement (envoi de l'évènement) au niveau du result (et du fault)?

    Fabien

  5. Bonjour Fabien et merci de ta réponse

    Tu veux dire faire ceci ?

    Actionscript:
    1. private function result(event:ResultEvent):void
    2.         {
    3.             _retour = event.result as Boolean;
    4.                         if(_retour == false)
    5.                 _results.push(new ValidationResult(true, null, "PasUnique""Le pseudo entré existe déjà pour l'univers "));
    6.  
    7.         }

    J'y avais déjà pensé mais malheureusement dans ce cas de figure, le return _results de la fonction doValidationfonction s'effectue avant l'appel de la fonction result (ou du moins son exécution complète) et du coup renvoie un tableau vide donc je me retrouve dans la situation inverse, jamais d'erreur de validation levée cette fois ci.

    Je me demandais donc s'il y avais moyen de sortir du mode asynchrone pour une classe ?

    Merci Fabien

  6. Hum tu peux essayer avec une fonction anonyme (c'est pas beau du tout):
    _ro.addEventListener(ResultEvent.RESULT, function(event:ResultEvent):void {
    #
    retour = event.result as Boolean;

    if(_retour == false)

    _results.push(new ValidationResult(true, null, "PasUnique", "Le pseudo entré existe déjà pour l'univers "));
    });

    Il y a une chance que cela fonctionne. Le mieux est d'appeler directement la méthode handleResults(errorResults:Array):ValidationResultEvent qui est celle appellée avec le retour du doValidation().
    Il faut jeter un oeil au code de Flex, ça aide pour comprendre :)

    Fabien

  7. Encore merci de ta réponse Fabien.
    J'ai surchargé la méthode comme tu me l'as conseillé de cette manière :

    Actionscript:
    1. override protected function handleResults(errorResults:Array):ValidationResultEvent
    2.         {
    3.            
    4.             if(_retour == false)
    5.                 errorResults.push(new ValidationResult(true, null, "PasUnique""Le pseudo entré existe déjà pour l'univers "))
    6.            
    7.             var resultEvent:ValidationResultEvent = super.handleResults(errorResults);
    8.                        
    9.             return resultEvent;
    10.            
    11.         }

    Maintenant, je suis dans le cas où j'obtiens toujours le message de validation. Reste à savoir si je me suis planté en surchargeant la méthode ou si l'écouteur n'as toujours pas le temps de lancer la fonction result (ce que je crois).

    A noter un comportement bizarre (que j'ai cependant depuis le début de mes tests) : lors du premier click sur mon bouton de validation de mon formulaire, si j'ai le message de validation incorrecte lors d'un nouveau click il disparait et vice versa...

    Bref je patauge un peu :)

  8. Bonjour, voici mon code après quelques modifs :

    Actionscript:
    1. package myvalidators
    2. {
    3.  
    4.     import mx.events.ValidationResultEvent;
    5.     import mx.rpc.events.FaultEvent;
    6.     import mx.rpc.events.ResultEvent;
    7.     import mx.rpc.remoting.RemoteObject;
    8.     import mx.validators.ValidationResult;
    9.     import mx.validators.Validator;
    10.        
    11.     public class UniquePseudoValidator extends Validator
    12.     {      
    13.         private var _results:Array = new Array();
    14.         private var _ro:RemoteObject = new RemoteObject();
    15.         private var _retour:Boolean = new Boolean();
    16.         private var _value:Object = new Object();
    17.         private var _eventResult:Object = new Object();
    18.         public var universObj:Object = new Object();
    19.        
    20.        
    21.         public function UniquePseudoValidator()
    22.         {
    23.             super();
    24.         }
    25.        
    26.         override protected function getValueFromSource():Object
    27.         {
    28.             _value.pseudo = super.getValueFromSource();
    29.             _value.univers = universObj.univers;
    30.             _value.universCap = _value.univers.charAt(0).toUpperCase() + _value.univers.substr(1, _value.univers.length - 1);
    31.             _value.num = universObj.num;
    32.            
    33.             return  _value;
    34.         }
    35.        
    36.         override protected function doValidation(value:Object):Array
    37.         {         
    38.             _results = super.doValidation(value.pseudo);
    39.            
    40.             _ro.destination = "zend";
    41.             _ro.source = "ServiceClasse";
    42.             _ro.addEventListener(ResultEvent.RESULT, result);
    43.             _ro.addEventListener(FaultEvent.FAULT, fault);
    44.             _ro.isUniquePseudo(value)
    45.        
    46.             return _results;
    47.         }
    48.        
    49.         private function result(event:ResultEvent):void
    50.         {
    51.             _eventResult.result = event.result as Boolean;
    52.         }
    53.        
    54.         private function fault(event:FaultEvent):void
    55.         {
    56.             _eventResult.fault = event.fault as Boolean;
    57.         }
    58.        
    59.         override protected function handleResults(errorResults:Array):ValidationResultEvent
    60.         {
    61.            
    62.             if( (_eventResult.result != undefined) &amp;&amp; (_eventResult.result == false) )
    63.                 errorResults.push(new ValidationResult(true, null, "PasUnique""Le pseudo entré existe déjà sur l'univers " + _value.universCap));   
    64.            
    65.             if( (_eventResult.fault != undefined) &amp;&amp; (_eventResult.fault == false) )
    66.                 errorResults.push(new ValidationResult(true, null, "ErreurServeur""Une erreur serveur est survenue lors de la validation du compte"));
    67.            
    68.             var resultEvent:ValidationResultEvent = super.handleResults(errorResults);
    69.                        
    70.             return resultEvent;
    71.            
    72.         }
    73.                    
    74.     }
    75. }

    J'obtiens maintenant un résultat pour le moins surprenant : ca marche mais à partir du deuxieme clic sur mon bouton de validation de formulaire ^^

    En gros si j'entre un pseudo dans ma base et clic sur le bouton je n'ai pas d'erreur. Je reclique j'ai une erreur, je reclique j'ai toujours l'erreur. Idem si je remets ensuite un pseudo unique, je clique j'ai l'erreur puis ensuite elle disparait. J'y comprends pas grand chose...

  9. Bon finalement, je m'en suis sorti en effectuant ma requête non pas dans mon validateur mais dans mon mxml et ensuite j'exécute mon validateur dans le result ou le fault de mon service en lui fournissant la valeur booléenne obtenue par mon service :)

    Merci pour tes conseils Fabien

  10. C'est beaucoup plus propre comme cela oui :)

    Bonne chance pour la suite
    Fabien

  11. Dans cet exemple, pourquoi est ce que le trigger du validator est le bouton id=myButton ?

  12. Salut,
    En fait, la validation se fait automatiquement lorsque le champ texte perd le focus. Donc soit quand tu cliques sur le bouton, soit quand tu changes le focus, avec la touche tab par exemple

    Fabien


Leave a comment

(required)

Aucun trackbacks pour l'instant