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.
{
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.
{
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)
{
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.
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.
{
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.
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>
Update: 29/09/2009
{
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 {}

Eu sou Pedro Claudio, ColdFusion Developer baseado no Rio de Janeiro. 

