Better form validation in Flex

Flex comes with a built-in form validation system that unfortunately uses mystery meat tooltips to display component validation errors.

To see what I mean, take a look at the example I wrote a while back in the Validating Data Quick Start.

Mystery Meat Error Messages

If you tab around that example, you may notice that you are probably doing something wrong (as indicated by the red borders around the form items that begin to appear) but you have no idea what you're doing wrong. In order to find out, you have to mouse over the form field. Only then does a tooltip appear to tell you what the problem is.

Needless to say, having the user switch input devices and exert additional effort in order to see a form validation error does not make for good ergonomics or usability.

A better alternative is to actually display the error message beside the control.

You can, of course, do this simply by having the error message in a label.

For example:

<mx:FormItem label="Your name">
    <mx:HBox>
        <mx:TextInput id="yourName" change="validateNotEmptyTextInput(yourName);" focusOut="validateNotEmptyTextInput(yourName);"/>
        <mx:Label id="yourNameError" visible="false" text="Your name cannot be empty."/>
    </mx:HBox>
</mx:FormItem>

The validateNotEmptyTextInput() method, in this example, is not a Flex Validator (as used in my Validation Quick Start), but a simple method.

For example:

private function validateNotEmptyTextInput(target:TextInput):void
{
    (this[target.name+"Error"] as Label).visible = (target.text == "");
}

In the above example, I'm using a pragmatic naming convention to create a generic validation method that works with TextInput components. Nothing too spectacular visually, but usability-wise already better than the default Flex behavior.

Creating an error Label for each component, though, is not very practical. We can overcome this limitation and add a little visual flare by using the Flex error tooltip instead of a label.

Kyle Quevillon has a post in which he details how to use the Flex Tooltip Manager to create Error Tooltips. Referring to Kyle's post, we can alter the validateNotEmptyTextInput() method as follows:

// If the TextInput is empty, display an error message.
if (target.text == "")
{
    // Has the error message ToolTip already been created?
    // (A reference to created ToolTip instances is saved in a hash called errorMessageToolTips.)
    var toolTipExists:Boolean = errorMessageToolTips.hasOwnProperty(target.name);
 
    if (toolTipExists)
    {
        // Error message ToolTip has already been created, just show it.
        (errorMessages[target.name] as ToolTip).visible = true;
    }
    else
    {
        // Create the ToolTip instance.
        var pt:Point = new Point(target.x, target.y);
        pt = target.contentToGlobal(pt);
        var errorTip:ToolTip = ToolTipManager.createToolTip(errorMessages[target.name] + " cannot be empty", pt.x + target.width + 5, pt.y) as ToolTip;
        errorTip.setStyle("styleName", "errorTip");
 
        // Save a reference to the error message ToolTip in a hash for later use.
        errorMessages[target.name] = errorTip;
    }
}
else
{
    // TextInput is valid. Hide the error message if one exists.
    if (toolTipExists)
    {
        (errorMessageToolTips[target.name] as ToolTip).visible = false;
    }
}
 

Where errorMessages is a hash of personalized messages:

errorMessages =
{
    yourName: "Your name",
    yourEmail: "Your email",
    phone: "The phone number"
}

Thus, we no longer need the Label component but there is a catch: we still need the HBox (or a container of some sort) if the TextInput is in a FormItem. Otherwise, contentToGlobal() returns an incorrect value when trying to position the error message.

So the new FormItem looks something like this:

<mx:FormItem label="Your name">
    <mx:HBox>
        <mx:TextInput id="yourName" change="validateNotEmptyTextInput(yourName);" focusOut="validateNotEmptyTextInput(yourName);"/>
    </mx:HBox>
</mx:FormItem>

And the resulting validation errors both look nice and are functional.

Better Flex Validation Errors

(In the above example, I modified the errorTip style to make the message boxes slightly shorter than usual by setting the paddingTop and paddingBottom properties to 2.)

To summarize, I'm not a big fan of the mystery meat validation error tooltips in Flex. Displaying validation errors on the form itself leads to better usability as users do not have to exert additional effort to find out what they did incorrectly.

Update: Someone has filed a bug about this very issue in the public Flex Bug and Issue Management System. I've left a comment on the bug and linked it to this post.

Creative Commons LicenseThe Better form validation in Flex article by Aral Balkan, unless otherwise expressly stated, is licensed under a Creative Commons Attribution-Noncommercial 2.0 UK: England License.

Post Metadata

Date
January 10th, 2008

Author
Aral

Category


2 Trackbacks & Pingbacks

  1. March 11, 2009 10:56 pm

    Enhance the default Flex form validation — Fine Tooth Floam :

  2. May 12, 2009 1:01 pm

    Verbeterde validatie in Flex « Document & Messaging Integration :

32 Comments

  1. Thanks for the tip (heh), Aral :-)



  2. aYo

    “Abstraction for the sake of abstraction is nothing but an ego trip for Real Programmers. For everyone else, it’s a pain the ass, lost productivity, and lost time.”

    I SOOOOOOO agree.


  3. That’s awesome!

    However, if you resize browser windows the tooltips do not adjust to the new position of form elements.

    Any idea how to sort it out?

    Dan



  4. Ilan

    Very useful !
    However, I think there’s a slight error in this example code:
    You use the errorMessageToolTips as a hash for the Tooltips created, but later on, when it’s time to retrieve the Tooltip or save it to this hash, you use the errorMessages hash instead:
    // Error message ToolTip has already been created, just show it.
    (errorMessages[target.name] as ToolTip).visible = true;
    And
    // Save a reference to the error message ToolTip in a hash for later use.
    errorMessages[target.name] = errorTip;

    Where it should be:

    // Error message ToolTip has already been created, just show it.
    (errorMessageToolTips[target.name] as ToolTip).visible = true;
    And
    // Save a reference to the error message ToolTip in a hash for later use.
    errorMessageToolTips[target.name] = errorTip;


  5. I had created a demo for my CForm component - http://flexed.wordpress.com/2008/01/02/component-cform-v10/…. This demo has an example of form validation. Check the source of the demo. Demo available @ http://www.udayms.com/flex/cform/cform.html




  6. Gambit

    I can’t figure this out. I’m using the final version of flex 3 but I keep getting an error on “var toolTipExists:Boolean = errorMessageToolTips.hasOwnProperty(target.name);” It says that it’s null.

    is anyone else getting this error or am I missing something



  7. Gambit

    finally got it…here is a full example.



  8. Gambit

    import mx.collections.ArrayCollection;
    import mx.controls.ToolTip;
    import mx.managers.ToolTipManager;
    import mx.controls.Alert;

    //private var errorTip:ToolTip;
    private var myError:String;
    private var errorMessageToolTips:Array;
    private var errorMessages:Array;

    private function doInit():void{
    errorMessages = new Array();
    errorMessages["yourName"] = “Your name”;
    errorMessages["yourphone"] = “The phone number”;
    errorMessageToolTips = new Array();
    errorMessageToolTips["yourName"];
    errorMessageToolTips["yourphone"];

    }
    private function validateNotEmptyTextInput(target:TextInput):void{

    // If the TextInput is empty, display an error message.
    if (target.text == “”){
    // Has the error message ToolTip already been created?
    // (A reference to created ToolTip instances is saved in a hash called errorMessageToolTips.)
    var toolTipExists:Boolean = errorMessageToolTips.hasOwnProperty(target.name);

    if (toolTipExists){
    // Error message ToolTip has already been created, just show it.
    (errorMessageToolTips[target.name] as ToolTip).visible = true;
    }else{
    // Create the ToolTip instance.
    var pt:Point = new Point(target.x, target.y);
    pt = target.contentToGlobal(pt);
    var errorTip:ToolTip = ToolTipManager.createToolTip(errorMessages[target.name] + ” cannot be empty”, pt.x + target.width + 5, pt.y) as ToolTip;
    errorTip.setStyle(”styleName”, “errorTip”);

    // Save a reference to the error message ToolTip in a hash for later use.
    errorMessageToolTips[target.name] = errorTip;
    }
    }else{
    // TextInput is valid. Hide the error message if one exists.

    toolTipExists = errorMessageToolTips.hasOwnProperty(target.name);
    if (toolTipExists){

    (errorMessageToolTips[target.name] as ToolTip).visible = false;
    }
    }
    }


  9. Thanks for the workaround !!! I was starting to go crazy with my tooltip not EXACTLY where i wanted it to be ! :) Saved my day !


  10. i was talking about the HBox workaround (HBox word has been removed from previous post)



  11. Hmm

    Your point about the red borders not being enough information is a good one, but what if your form is complicated with about 10 or 15 fields? You can’t display them all (especially if the page just loaded). It’d be good if an error tip only displayed like this for the component that currently has the focus.



  12. justin

    abstraction for those that know what they’re doing saves a lot of time and money. Real programmers know that…


  13. Hi, send me tutorial of Flex?
    thank you
    Leandro


  14. Thanks!!! This is good stuff.



  15. Hmm

    Abstraction??
    This won’t work for large forms. A more sophisticated solution is needed.



  16. Tami

    Abstraction also adds scalability to a solution–and as “Hmm” pointed out this “simple” solution won’t scale to large forms. My plug for “complex” yet “elegant” solutions…



  17. switcher

    Thanks a lot for this tip, but you have to write :
    var toolTipExists:Boolean = errorMessageToolTips.hasOwnProperty(target.name);
    before
    if (target.text == “”)

    if not, it can’t work because var toolTipExists doesn’t exist !
    else
    {
    // TextInput is valid. Hide the error message if one exists.
    if (toolTipExists)
    {
    (errorMessageToolTips[target.name] as ToolTip).visible = false;


  18. if i put the form in the TitleWindow, when i drag the window the toolTip is not following the window, how to manage this situation?



  19. Hmm

    This is fine for small forms like this.

    But what’s needed is error tips that optionally display for a few seconds, and disappear automatically. For large forms/screens often present in APPLICATIONS (not little WEBSITE type forms like this), this would improve usability massively. In this case, error tips should also stay up for the ACTIVE field, just like the example here, but only for the field that has focus. This should all be configurable through Flex property settings…

    So options would include:
    1) Default Flex behavior
    2) Permanent error tips, like this blog has
    3) Temporary error tips, which show for a few seconds but revert to look like the Flex default after that time has expired (mouse-over should still show the field again like it does by default)
    4) Permanent error tips for the FOCUSED component only (this option would be compatible with both 1 and 3)



  20. Hmm

    And I realize this level of flexibility isn’t trivial, which is why it really needs to be in Flex 4 and not on blogs.



  21. Claudio Parnenzini

    Hi,

    I just added the capability to the validate() method to show the error message into a ToolTip when typing. As I’m not a Flex expert, maybe there is a better way to do that.

    You should add to your class a ToolTip var named tmpToolTip, to work correctly.

    Here is the code:

    // Helper method. Performs validation on a passed Validator instance.
    // Validator is the base class of all Flex validation classes so
    // you can pass any validation class to this method.
    private function validate(validator:Validator):Boolean
    {
    // Get a reference to the component that is the
    // source of the validator.
    var validatorSource:DisplayObject = validator.source as DisplayObject;

    // Suppress events if the current control being validated is not
    // the currently focussed control on the form. This stops the user
    // from receiving visual validation cues on other form controls.
    var suppressEvents:Boolean = (validatorSource != focussedFormControl);

    // Carry out validation. Returns a ValidationResultEvent.
    // Passing null for the first parameter makes the validator
    // use the property defined in the property tag of the
    // tag.
    var event:ValidationResultEvent = validator.validate(null, suppressEvents);

    // Supress the old tooltip
    if(tmpToolTip != null && !suppressEvents) {
    ToolTipManager.destroyToolTip(tmpToolTip);
    tmpToolTip = null;
    }
    // Show the tooltip with the validation error message
    if(!suppressEvents && (event.type == ValidationResultEvent.INVALID)) {
    var point:Point = validatorSource.localToGlobal(new Point(0,0));
    tmpToolTip = ToolTipManager.createToolTip(event.message, point.x+validatorSource.width+5, point.y) as ToolTip;
    }

    // Check if validation passed and return a boolean value accordingly.
    var currentControlIsValid:Boolean = (event.type == ValidationResultEvent.VALID);

    // Update the formIsValid flag
    formIsValid = formIsValid && currentControlIsValid;

    return currentControlIsValid;
    }


  22. Thanks Aral! Exactly what i was looking for.
    In case anyone is curious about the errorTip style ( ‘errorTip.setStyle(”StyleName”, “errorTip”); )
    it’s defined by the framework ( http://livedocs.adobe.com/flex/3/html/help.html?content=tooltips_5.html )



  23. Ingo

    Hey,
    This is a good solution to the bad-form-problem.
    But its unusable for big forms since the tooltips dont scroll with the rest of the form.
    Is there already a solution to this problem out there?



  24. Babu Nazeer

    Hey,

    You did with custom validation by (if (target.text == “”)). But the Validator component has ample feature like zip code,Number etc…,I trying to use Validator in conjunction with your tool tip example,I end up with default tool tip generated by Validator component, is there any possibilities to remove from stack.



  25. tony

    The link: “Kyle Quevillon has a post in which he details how to use the Flex Tooltip Manager to create Error Tooltips. ” doesn’t work, any idea where Kyle has moved his entry to?



  26. tony


  27. Aral Ashole

    Hey bitch, the Link http://blog.739saintlouis.com/2007/09/ is empty. put the source don’t try to be so smart


  28. My, my, aren’t we an imaginative comment poster? Afaik, BT owns the patent for hyperlinks so I’d take the issue up with them. (PS. “asshole” has two S’s in it; learn to spell!)


  29. Thanks again for the code. I built this functionality, with a few changes, into a component (extending PromptingTextInput from flexlib) for anyone to use.
    Hope it helps someone.
    http://toddrothe.com/blog/?p=14



  30. Great post, but sometimes you don’t have room to put error tool tips off to the side. That is why I created thinner error tool tips tonight. Check them out here:
    http://www.tonyamoyal.com/blog/?p=26


Leave a Reply

Anti-Spam Protection by WP-SpamFree