MXML , ActionScript 3 , Flex

Automatizando formulários Flex

8 de Setembro de 2009

Desde meu último post sobre Flex contendo script, faz algum tempo, para quebrar esse gelo, vou mostrar para você "o caminho" para automatizar a criação de formulários no Flex.

Para isso vamos começar pela classe Event. Vamos chamar essa classe de CustomEvent, seu objetivo será criar o validador dentro do evento (mx.events.FlexEvent) creationComplete do campo, para foi necessário adicionar três propriedades à classe, que são: validateType do tipo Object que receberá as informações para configurar o validador, o validator do tipo coringa para que fique mais fácil de trabalhar com vários validadores diferentes (aqui vou trabalhar apenas com mx.validators.StringValidator e mx.validators.EmailValidator), e a constante contendo o nome do evento que vai caracterizar a configuração chamasse VALIDATECHANGE.

package core.events
{

    import flash.events.Event;
    
    import mx.events.FlexEvent;
    import mx.validators.EmailValidator;
    import mx.validators.StringValidator;
    
    public class CustomEvent extends FlexEvent
    {
        static public const VALIDATECHANGE:String = 'validatechange';
        
        private var _validateType:Object;
        
        private var _validator:*;
        
        public function CustomEvent(type:String,validateType:Object, bubbles:Boolean=false,cancelable:Boolean=false)
        {
            
            super(type, bubbles,cancelable);
            setValidator(validateType);
            
        }

        public override function clone() : Event {
            return new CustomEvent(CustomEvent.VALIDATECHANGE,validateType);
        }

        public override function toString():String {
            return formatToString('CustomEvent','type', 'bubbles', 'cancelable', 'validateType','validator');
        }

        public function get validateType():Object
        {
            return _validateType;
        }

        public function get validator():*
        {
            return _validator;
        }

        private function setValidator(params:Object):void
        {
            if(params.validateType.length < 1){
                return;
            }
            _validateType = params;
            switch(params.validateType){
                case 'string':
                case 'noblank':
         _validator = new StringValidator();
             if(params.message.length >
0){
                     _validator.requiredFieldError =
                 _validator.requiredFieldError =
                 _validator.tooLongError =
                 _validator.tooShortError = params.message;
             }
                     _validator.minLength = 1;
                    break;
                case 'email':
     _validator = new EmailValidator();
         if(params.message.length >
0){
         _validator.requiredFieldError =
         _validator.invalidCharError =
         _validator.invalidDomainError =
         _validator.invalidIPDomainError =
         _validator.invalidPeriodsInDomainError =
         _validator.missingAtSignError =
         _validator.missingPeriodInDomainError =
         _validator.missingUsernameError = params.message;
         }    
                    break;
            }
_validator.source = params.source;
_validator.property = params.property;
    _validator.required = params.required;
            _validator.triggerEvent = params.triggerEvent;
     _validator.enabled = true;
        }        
    }
}

Os validadores são classes/tags separadas do campo, para agilizar o desenvolvimento precisamos dos validadores nos campos, para isso vamos manipular uma classe baseada no TextInput (spark.components.TextInput), de forma a permitir a passagem da configuração básica do validador na tag, trabalharemos com a tag CustomInput adicionando esses atributos, que são eles: validateType que distinguirá o tipo do validador, message contendo a mensagem que será enviada ao navegador em caso de erro, required caracteriza como requerido ou não, index que ocupará o lugar do id. Para manipulação interna foram criadas as propriedades: _initialized responsável por informar se é a primeira vez que o validor será criado,_params responsável por armazenar as configurações para a criação do validador, e _validator o próprio validador.

package core.mxml
{

    import core.events.CustomEvent;
    import core.model.CustomModelLocator;
    
    import mx.events.FlexEvent;
    import mx.utils.ObjectUtil;
    
    import spark.components.TextInput;

    [Event(name="validatechange", type="core.events.CustomEvent")]
        
    public class CustomInput extends TextInput
    {

        private var _initialized:Boolean = false;
        private var _params:Object = {validateType:'',message:'',source:'',index:'',property:'text',required:false,triggerEvent:'change'};

        private var _validator:* ;
        private var _model:CustomModelLocator;

        private var _validateType:String;
        private var _message:String = '';
        private var _required:Boolean = false;
        private var _index:int;
        private var _source:String;
        
        public function CustomInput()
        {
            _model = CustomModelLocator.getInstance();
            super();
            addEventListener(FlexEvent.CREATION_COMPLETE,onCreationComplete);
        }
        
        private function dispatchEventCustom():void{
            _params.validateType = _validateType;
            _params.message = _message;
            _params.required = _required;
            _params.source = this;
            _params.index = _index;
            dispatchEvent(new CustomEvent(CustomEvent.VALIDATECHANGE,_params));
        }

        private function onCreationComplete(event:FlexEvent):void {
            id = this.id = super.id = name;
            addEventListener(CustomEvent.VALIDATECHANGE,setValidator);
            dispatchEventCustom();
             _initialized = true;
        }
        
        public function get validator():* {
            return _validator;
        }

        private function setValidator(event:CustomEvent):void
        {
            enabled = true;
            _validator = event.validator;
            _model[_source][event.validateType.index].validator = event.validator;
            //event.currentTarget.parent.parent.dataConfig[event.validateType.index].validator = event.validator;
        }
        
        public function get validateType():String
        {
            return _validateType;
        }
        
        [Bindable(event="validatechange")]
        [Inspectable(enumeration="noblank,string,email", name='validateType')]
        public function set validateType(v:String):void
        {
            _validateType = v;
            if(_initialized){
                dispatchEventCustom();
            }
        }
        
        public function get message():String
        {
            return _message;
        }

        [Inspectable(type='String', name='message')]
        public function set message(v:String):void
        {
            _message = v;
        }

        
        public function get source():String
        {
            return _source;
        }

        [Inspectable(type='String', name='source')]
        public function set source(v:String):void
        {
            _source = v;
        }




        public function get required():Boolean
        {
            return _required;
        }
        
        [Inspectable(type='Boolean', name='required')]
        public function set required(v:Boolean):void
        {
            _required = v;
        }

        public function get index():int
        {
            return _index;
        }

        public function set index(v:int):void
        {
            _index = v;
        }


    }
}

Os campos serão criados no modelo da classe Form (mx.containers.Form), e como quero deixar meu formulário mais genérico, ou seja, quero que os ids, labels e validadores, sejam flexíveis, possibilitando uma criação dinâmica. Foi criada então a tag CustomFormBase, que receberá os dados para configuração do formulário, dinamicamente, em uma única propriedade, e ainda receberá a ação do submit (que não foi trabalhada tão profundamente)

package core.mxml
{
    import core.model.CustomModelLocator;
    
    import flash.events.MouseEvent;
    
    import mx.collections.ArrayCollection;
    import mx.containers.Form;
    import mx.controls.Alert;
    import mx.events.FlexEvent;
    import mx.validators.Validator;
    
    import spark.components.Button;

    public class CustomFormBase extends Form
    {
        public var send:Button;
        private var _source:ArrayCollection;
        private var _model:CustomModelLocator;
        private var _dataConfig:String;
        
        
        
        public function CustomFormBase()
        {
            _model = CustomModelLocator.getInstance();
            super();
            addEventListener(FlexEvent.CREATION_COMPLETE,doCreationComplete);
        }
        
        private function doCreationComplete(event:FlexEvent):void
        {
            send.addEventListener(MouseEvent.CLICK,submit);
        }
        
        protected function submit(event:MouseEvent):void
        {
             var _validators:Array = new Array();
            for(var i:int=0;i<_source.length;i++){
                _validators.push(_source[i].validator);
            }
_validators = Validator.validateAll(_validators);
if (_validators.length == 0) {
Alert.show("Preenchimento correto", "SUCCESSO");
}
        }        

        
        public function get source():ArrayCollection
        {
            return _source;
        }
        
        public function get dataConfig():String
        {
            return _dataConfig;
        }
        
        [Bindable]
        [Inspectable(type='String', name='dataConfig')]
        public function set dataConfig(v:String):void
        {
            _dataConfig = v;
            _source = _model[v];
        }

        
    }
}

Nosso primeiro MXML é bem simples, utilizando a classe Repeater (mx.core.Repeater), dispondo as tag ada maneira que desejamos que sejam vizualizadas.

<mxml:CustomFormBase
        xmlns:fx="http://ns.adobe.com/mxml/2009"
        xmlns:s="library://ns.adobe.com/flex/spark"
        xmlns:mx="library://ns.adobe.com/flex/halo"
        xmlns:mxml="core.mxml.*">

        <mx:Repeater id="configure" dataProvider="{source}">
            <mx:FormItem label="{configure.currentItem.label}" width="100%">
                <mxml:CustomInput width="100%"
                    source="{dataConfig}"
                    name="{configure.currentItem.id}"
                    message="{configure.currentItem.message}"
                    required="{configure.currentItem.required}"
                    validateType="{configure.currentItem.validateType}"
                    text="{configure.currentItem.text}"
                    index="{configure.currentIndex}" />

            </mx:FormItem>        
</mx:Repeater>
        <mx:FormItem width="100%" horizontalAlign="right">
            <s:Button id="send" label="Submit" />    
        </mx:FormItem>
</mxml:CustomFormBase>

Já que criamos um padrão de construção, vamos continuar nele, separando o ActionScript do MXML, durante a criação do arquivo de referência da aplicação, adicionamos os dados (fake) fixos.

package core.mxml
{
    import core.model.CustomModelLocator;
    
    import mx.collections.ArrayCollection;
    
    import spark.components.Application;

    
    
    public class CustomApplication extends Application
    {
        
            
        [Bindable] private var _model:CustomModelLocator;
        
        public function CustomApplication()
        {
            _model = CustomModelLocator.getInstance();
            super();
        }
    
        public function get dataConfig1():String
        {
            return _model.DATASOURCE1;
        }
        
        public function get dataConfig2():String
        {
            return _model.DATASOURCE2;
        }

    }
}

Por fim nossa chamada mãe.

<mxml:CustomApplication
    xmlns:fx="http://ns.adobe.com/mxml/2009"
    xmlns:s="library://ns.adobe.com/flex/spark"
    xmlns:mx="library://ns.adobe.com/flex/halo"
    xmlns:mxml="core.mxml.*" viewSourceURL="srcview/index.html">

    <mx:HDividedBox>
        <s:Panel width="350">
            <mxml:CustomForm dataConfig="{dataConfig1}" width="100%" />
        </s:Panel>
        <s:Panel width="350">
            <mxml:CustomForm dataConfig="{dataConfig2}" width="100%" />
        </s:Panel>
    </mx:HDividedBox>
</mxml:CustomApplication>

exemplo
source

Update: 29/09/2009

package core.model
{
    import mx.collections.ArrayCollection;

    [Bindable]
    public class CustomModelLocator
    {
        public const DATASOURCE1:String = 'dataConfig1';
        public const DATASOURCE2:String = 'dataConfig2';
        
        public var dataConfig1:ArrayCollection;
        
        public var dataConfig2:ArrayCollection;            
        
        static private var instance:CustomModelLocator;
        
static public function getInstance() : CustomModelLocator
{
if ( instance == null ){
                instance = new CustomModelLocator(new CustomSingleton());
}
return instance;
}
        
        
        public function CustomModelLocator(singleton:CustomSingleton)
        {
if ( singleton == null ){
throw new Error( "Construção inválida" );
}else{
                dataConfig1 = new ArrayCollection([
                    {label:'Nome' ,message:'Nome inválido' ,validateType:'noblank',id:'nome' ,text:'Pedro Claudio',required:true},
                    {label:'E-mail',message:'E-mail inválido',validateType:'email' ,id:'email',text:'' ,required:true}
                ]);    
                
                dataConfig2 = new ArrayCollection([
                    {label:'Endereço',message:'Endereço inválido',validateType:'noblank',id:'end' ,text:'',required:true },
                    {label:'Bairro' ,message:'Bairro inválido' ,validateType:'noblank',id:'bairro',text:'',required:false},
                    {label:'Cidade' ,message:'Cidade inválida' ,validateType:'noblank',id:'bairro',text:'',required:true }
                ]);     
}            
        }
        
    }
}
class CustomSingleton {}

871 Download

Enviar

Compartilhe sua opiniao

Comentar

Subscribe

Acompanhe blog.

Digitrum Servers Powered by Blogcfc 5.9.2.002