You want to create more agile forms, and configure the validators to integrate dynamically into the field from a data source.
Initially write a class event to execute when creating the fields complete the creation, it will be set up and validation of the field, then write a class extending the class TextInput added the event created, create a class based on the Form to receive the settings in the form of only one variable, write a MXML nesting fields in a Repeater inside the FORM tag that you customized.
For those who are going to start with the class Event. We will call this class CustomEvent, your objective will be to create a validator inside the event (mx.events.FlexEvent) creationComplete of the field, was necessary add three properties of the class, which are: validateType of the Object type who will receive the information to configure validator, validator of the special type so that it get easier to working with several validators different (here I am going to just to work with mx.validators.StringValidator and mx.validators.EmailValidator) and the constant containing event's name that is going to characterize the configuration called 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;
}
}
}
Validators are classes/tags separated of the field, to speed the development we will need the validators in the field, for that are going to manipulate a class based on TextInput (spark.components.TextInput), of form to allow of the basic configuration of validator in tag, we will work with for tag CustomInput adding these attributes, which are them: validateType that will distinguish the type of validator, message containing the text that will be sent to the navigator in case error, required characterizes as required or not. For internal manipulation were created the properties: _initialized responsible in inform if is the first time that the valitor will be created, _params responsible in the storage the configurations for the creation of validator, e _validator o validator.
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;
}
}
}
The field will be created in the model of the class Form (mx.containers.Form), and as I want to let my form more generic, in other words, I want ids, labels and validadores more flexible, enabling a dynamic creation. Then it was created the tag CustomFormBase, which will receive the data for configuration of form, dynamically, in an unique property, and still will receive the action of submit (which was not worked so profoundly).
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];
}
}
}
Our first MXML is very simple, using the class Repeater (mx.core.Repeater), disposing the tag the way we wish for they are visualized.
<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>
We already create a construction standard, so we will continue by separating the ActionScript of the MXML during the creation of the reference file of the application, we add the data (fake) fixed.
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;
}
}
}
Finally our mother call.
<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>
And model class.
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 {}
+