Thursday, August 6, 2009

Always showing error tips (validators)

Many people have complained about how Flex 3's error validation messages are not shown unless you hover your mouse over the input field that is invalid. See the image on the right and you'll probably know what I'm talking about. If not, then when you use a mx.validators.Validator on an input field such as a TextInput or TextArea and the user enters invalid data then the input control gets a red border indicating an error. But the error message (e.g. "This field is required") is ONLY displayed when the user hovers the mouse over the input field.

After reading a few other posts on the subject, including a bug report that is in the Adobe Bug Tracker I decided to see if I could come up with a nice easy solution.

I made a new class called ErrorTipManager that has a bunch of public static functions that lets you easily attach your validators (or your own validation functions). The code is documented, so view the sample below and right click view source to get the source code. The error tips should stay in the correct position even when the input moves (from a window resize or layout).

A lot of credit goes to Aral Balkin's blog entry on Better form validation in Flex. I also got help from this blog on how to show error tooltips.

Note that if you use the ErrorTipManager as described above and you set the validator.enabled = false; then the error tip will still be displayed. The validator does not fire an event when the enabled value changes, so it isn't possible to automatically update the visibility of the error tip. Instead you can do it manually by calling ErrorTipManager.hideErrorTip(validator.source, true). See the source code of the example above - I've added in a CheckBox that controls the second validator's enabled state.

August 26th, 2009 update
Fixed a problem where the error tip wasn't being position properly.

November 2nd, 2009 update
Added two new functions to ErrorTipManager for handling error tips in popup windows:
  • registerValidatorOnPopUp(validator, popUp, hideExistingErrorTips) - registers the validator and adds move and resize listeners on the popUp. It can also hide any existing error tips (this is a good idea because the existing error tips appear on top of the popUp window which doesn't look good).
  • unregisterPopUpValidators(popUp, validateExistingErrorTips) - unregisters all the validators associated with the popUp and removes the move and resize listeners that were added to the popUp. It can also validate all the remaining validators which will cause the error tips to re-appear now that the popUp has closed.

March 24th, 2010 update
Fixed a problem where the error tip wasn't being positioned properly when the parent container was scrolled. It will also hide the error tip if the source component gets scrolled out of the view. I also now move the error tip in front of other error tips when it gets positioned.

July 14th, 2010 update
Added two more event listeners in the ErrorTipManager to listen for when the input is hidden (visible="false") and when it gets removed from its parent (input.parent.removeChild(input)).
This still does not solve the problem where the error tip remains visible when the parent is hidden or removed (e.g. if the input is inside a TabNavigator and the tab changes). In this case I'd suggest manually hiding or removing the error tip. Listen for the appropriate event like "change" and then call ErrorTipManager.hideErrorTip(input).

March 2011 update
I've posted an alternate approach to displaying validation error messages that doesn't involve the rather hacky approach above. You can view the blog post here.


Silva Developer said...

Great example, congratulations.

Help me so much!


Silva Developer

FredMBarrett said...

Great work. I did have a problem validating a TextInput control in a modal dialog. The code can't seem to place the error tip in the correct place. From what I can tell, it looks as though the coordinates are that of the parent container, not the modal container.


Scott Esker said...

Great solution -- I have the same problem of the tooltip location is outside the parent form. Any ideas?

Anonymous said...

The implementation of the method getErrorTipPosition isn't quite correctly defined.

Here's what it should be.

private static function getErrorTipPosition(target:DisplayObject):Point {
// position the error tip to be in the exact same position as the real error tooltip
var pt:Point = new Point();
if (target) {
// need to get the position of the target in global coordinates
//var globalPos:Point = target.localToGlobal(new Point(target.x, target.y)); Not correct (PG).
var globalPos:Point = target.localToGlobal(new Point(target.width, target.y)); //Use the target.width instead (PG).
// position to the right of the target
//pt.x = globalPos.x + target.width + 4; ///Not correct (PG).
pt.x = globalPos.x + 8;// No need to add the target.width, increase the offset (PG).
pt.y = globalPos.y - 2;
return pt;

Patrick Groves

Chris Callendar said...

Thanks for your comments, I've now updated the example above to work properly in (hopefully) all situations, including in a popUp dialog.

Anonymous said...

Great work, nice and clean solution!
Still have a small problem getting it to look perfect though.
The error text is displayed in a red tooltip area that is 30-40% to wide.
("balloon" width is 130-140% of the text width)
Any idea how I can correct this?

Chris Callendar said...

I haven't seen a problem like that before where the balloon is too big. I don't calculate the size of the error tip, it should automatically size itself based on the text.
You might try changing the code in the ErrorTipManager.createErrorTip() function and see if you can make the error tip the correct size.

Anonymous said...

Hi Chris,
Thanks for you comment.
I already added some code to get it right, like you suggest, but it's strange that it happens in the first place.

added: errorTip.width = .9 * errorTip.width;
added: errorTip.height = .9 * errorTip.height;

Looks perfect now.

But can you explain what the tt var does? It looks like you make it the same ToolTip as "errorTip" and then you set the style, but you don't use tt var later on in the return or display. Or am I missing something here?

// set the styles to match the real error tooltip
var tt:ToolTip = ToolTip(errorTip);
tt.styleName = "errorTip";

Thanks for sharing your code and helping out.

Chris Callendar said...

Hi Oscar,

The only point of the variable tt is so that I can set the styleName to "errorTip", this makes the tooltip look like the red error tips. The errorTip variable is of type IToolTip, which doesn't expose the styleName property.
I probably should have just used one line to simplify it:
ToolTip(errorTip).styleName = "errorTip";

Anonymous said...

The code didn't take into consideration about listeners (for my custom validator validating multiple controls), so it needs to be updated:

* Called when the validator fires the valid event.
* Hides the error tooltip if it is visible.
public static function validHandler(event:ValidationResultEvent):void {
// the target component is valid, so hide the error tooltip
var validator:Validator = Validator(;
hideErrorTip(validator.listener != null ? validator.listener : validator.source);
// ensure that the source listeners were added

* Called when the validator fires an invalid event.
* Shows the error tooltip with the ValidatorResultEvent.message as the error String.
public static function invalidHandler(event:ValidationResultEvent):void {
// the target component is invalid, so show the error tooltip
var validator:Validator = Validator(;
showErrorTip(validator.listener != null ? validator.listener : validator.source, event.message);
// ensure that the source listeners were added

Chris Callendar said...

Thanks for the last post. I didn't know what the listener property was for until I saw your post.

Anonymous said...

Thanks for the tips! When i have more than one textInput field in the popup, the tooltip of the second field doesnt move when i move the popup, am I missing something?


Chris Callendar said...

Good catch. I had only added support for one validator per popup.

I've made a change now in the ErrorTipManager so that it now supports multiple validators per popup. I renamed the unregisterValidatorOnPopUp(validator, popUp) function to unregisterPopUpValidators(popUp). The validator no longer needs to be passed in since the ErrorTipManager keeps a record of all the validators that are registered to the popup.

I also updated the example shown above. Thanks for your comment.

Anonymous said...

Thank you very much for your quick response! I am a fairly new to Flex , I was trying to fix it but I couldn't get the tool tip to move with the control.

This work perfectly well with the application I am working on right now!

Again, thank you for sharing!

- Priscilla

Noella said...

I can't see any explicit licensing on this piece code so was wondering if it was ok to use in commerical application.


Chris Callendar said...

Sure Noella, you can use it in a commercial application.

Steve-O said...

This is great, exactly what I needed. I also have a little bit of an issue with the positioning, the "real" error tip is placed above my text box because there is not enough room on the screen to fit it to the right. But this code still wants to put it off to the right. I also had the issue with the balloon being too wide (but mine was the correct height).

Anyway, minor issues I'm sure I can fix. At any rate you saved me a bunch of time. Thanks!

Tam said...

I have been looking for a solution like this but I seem to be having trouble viewing the example and source code. Is the server down temporarily or did you move the code somewhere else?

Tam said...

it works now...Great work, Thanks a lot

Anonymous said...

It seems that I find a bug:
when use mx.validators.Validator.validateAll to validator,the error tooltip does not disapear any more

how to fix it?

digio said...

The validation messages don't seem to reposition if the input fields are placed inside a scroller and you scroll up and down.

Is there a good workaround for this?


Chris Callendar said...

Hi Anonymous,
I've tested using the Validator.validateAll([ validator ]) function and it seems to work fine for me. The error tip shows up, and then once I enter in some text to make it valid the error tip goes away. If you are still having trouble please feel free to post your example and I'll try to debug it.

Chris Callendar said...

Hi Pete,

I've updated the example above to contain a scrolling box. I had to make a fix to the ErrorTipManager class to listen for scroll events on the parent containers. Seems to work quite nicely. It also will hide the error tip if the source component is scrolled out of the view.


Alex said...

Hi Chris,

great example, it help a lot, but I have issue with hideAllErrorTips method, it not disable tip on field that has focus.
Can you help with it?
Also I found another issue in method showErrorTip, it shows tip even if field is valid. I'm added
check on empty error:
if (errorTip && errorTip.text != ""){}
and now its work fine for me.


Prashant V. Patel said...

Great Example... Thanks a ton...

Chris Callendar said...

Hi Alex,

When I use the hideAllErrorTips() function it works fine for me. Are you doing anything special? I just added a button that when clicked calls ErrorTipManager.hideAllErrorTips(), and it doesn't seem to matter which field has focus, the error tip is hidden.

As for your second point - the issue with the showErrorTip() function. If that works for you then that is great, but I don't think it should care what the value of errorTip.text is. The point of the showErrorTip() function is to show the error tip! It should get called only when the validator is invalid, just like hideErrorTip() which gets called when the validator is valid.


Alex said...

Hi Chris,

according to first issue you couldn't replicate it, because when you click on button it gets the focus, and all works fine in that case. But I have another situation: I have popup window with fields, and when I press close button(TitleWindow has closeButton) and shows alert (and that's why I need to hide all error tooltip), but when i press close button, focus remains in not valid component, and errorTip didn't disapear.
I found workaround: just need to set stage.focus=null.

Regarding to second issue, maybe you right, in my situation, even if field is valid, it show empty error, and I think its wrong.


Chris Callendar said...

That makes sense Alex, I didn't realize it was a focus issue.

Anonymous said...

Great example!

Just one question:

Is there an easy to to add an icon in front of the error text?

Chris Callendar said...

There isn't an easy way to add an icon to the error tip.

If you look inside the ErrorTipManager.createErrorTip() function, you'll see the line
errorTip = ToolTipManager.createToolTip(error, position.x, position.y);
This is where the tooltip gets created, and is of type ToolTip. There is nothing in the ToolTip class that supports icons, so one solution is you could manually add your icon to the tooltip object right after this line, something like this:

// embed your icon, put at the top of
private static const ICON:Class;

// Inside the createErrorTip() function
var tt:ToolTip = (errorTip as ToolTip);
// add your icon to error tip
var image:IFlexDisplayObject = new ICON();
tt.addChild(image as DisplayObject);
// position the icon, choosing the x-value is tricky
// it depends on where the tooltip gets displayed
// on the right, below, or above the input field.
var ttW:Number = tt.getExplicitOrMeasuredWidth();
var ttH:Number = tt.getExplicitOrMeasuredHeight();
var padLeft:int = int(tt.getStyle("paddingLeft"));
image.move(padLeft + 4, (ttH - 16)/2);
// Adjust the tooltip's size to include the icon's width
tt.setActualSize(ttW + 18, ttH);
// add padding on left for the icon
tt.setStyle("paddingLeft", padLeft + 16);

Another option would be to created your own class that extends ToolTip, and do all the positioning and sizing in that class. But then you'd have to duplicate what the ToolTipManagerImpl.createToolTip() function does (adding it to the system manager).

Good luck!

A.J. said...

Regarding your posting on March 24, to correct the problem when scrolling the parent container. I integrated your changes and they work great except when you use the mouse scroll wheel. I can replicate the problem using your scroll box example. If you grab the scroll bar with the mouse, the error tip moves smoothly, but when you use the scroll wheel, it jumps around. Nevertheless, this is an excellent tool. Thanks for sharing it.

R.H.A said...

Great example! Thanks!

I noticed that when I use my mouse wheel to scroll up/down my parent application the ErrorTip Box moves to a incorrect position. Have you noticed it? Do you know how can i fix it?
I looked into the source code and there is a method that gets ScrollEvents. It works when I scroll it down with my mouse click.
Thanks in advance

Chris Callendar said...

Thanks for letting me know about the scroll wheel problem.
I've made a small update to the ErrorTipManager class to fix the problem. It's not a very clean solution, but does seem to work okay.

R.H.A said...

It works perfectly fine.
Great work

Claudio said...

Great class Chris. Unfortunately handling ErrorTips is not as easy as it seems. Dealing with usability and UI issues can be very frustrating.

Your class was a great shortcut to me and I can't thank you enough. I'm currently struggling with popup revalidating issues, and so far I've made two additions:

1) updateErrorTipPosition function
if (makeVisible && !errorTip.visible && (target as UIComponent).errorString != "") {

added the last condition to avoid previously shown ErrorTips to reappear after re-validating

2) resetErrorTips function

public static function resetErrorTips() :void {
for each (var errorTip:* in errorTips) {
if (errorTip && errorTip is IToolTip) {
errorTip.visible = false;
errorTips = null;
errorTips = new Dictionary();
errorTips.weakKeys = true;

added this to deal with problems that arise when you try to revalidate a form.

I´m not sure if there's a better way but those changes are working well so far.

Best regards, Claudio

Chris Callendar said...

Thanks for those updates Claudio, much appreciated. I agree, showing error tips is not nearly as easy as I originally thought it would be. More and more problems keep coming up.

rogerio said...

Hello Chris,
I´ve been working with you ErrorTipManager and it´s been of great use.
Right now, I´m trying to do something similar to the 'positionErrorTip' method.
I have a 'continue' button that only jumps to the next screen if all validators.results returns null.
I´ve been trying to reposition my 'continue' button to the center of right side of the screen without success.
How am I supposed to get how many pixels the user have scrolled?

Chris Callendar said...

Hi Rogerio,

If I understand you correctly - to get how many pixels the user has scrolled you can use the verticalScrollPosition property on the Container. For horizontal scrolling use the horizontalScrollPosition property.

The verticalScrollPosition is "the current position of the vertical scroll bar. This is equal to the distance in pixels between the top edge of the scrollable surface and the topmost piece of the surface that is currently visible.".

So you'd probably want to use that value and add half of your container's height to center the continue button vertically.


Beau Scott said...

Hey Chris,

Great util. One thing, this will introduce a good sized memory leak as all your event listeners to each of the controls and the objects used as dictionary keys all have strong references. If you profile your sample app and watch the instance count of TitleWindow as you click the "Show PopUp" button and close the windows several times. These references won't allow any of the controls parented by these transient containers, nor the containers themselves to ever be garbage collected.

The simple fix to this would be to create your dictionaries specifying the first param to use weak references for the keys and to register each event listener with weak references. E.g.:

private static var errorTips:Dictionary = new Dictionary(true);


validator.addEventListener(ValidationResultEvent.VALID, validHandler, false, EventPriority.DEFAULT, true);

These are just a couple of the places the change needs to be made, just syntax examples.

Chris Callendar said...

Hi Beau,
Thanks very much for the comment. I haven't used weak references much, and didn't even think about it. But you're right, I ran the profiler and opened the popup window 10 times and found many instances of the TitleWindow lingering.
I've updated the code above with your suggestions.
Thanks again,

Mukund said...

Thank you very much. Your solution was really helpful


Mukund said...

I have another issue with this component

I have a component objCanvas:Canvas within which I am using ErrorTipManager to perform validation and display errors.
When there is an error and when the error message is being dispalyed, if I navigate to some other component. I see the red coloured error box still. I doesn't vanish


Chris Callendar said...

Hi Mukund,

I think the problem you are noticing is actually the way it is supposed to work.

By default when a validator runs and is invalid, the red error border shows up around the input field. My ErrorTipManager simply makes the Error tool tip show up when the input field isn't in focus.

Try your problem without using the ErrorTipManager and I think you'll find that the red error border will show up on a field even after it loses focus.

One thing you could do to get around this is to clear the errorString property on the UIComponent input field. The red border automatically shows up when that errorString property isn't null or empty.


Mukund said...

Hi Chris,

I think I was not clear with my point.

For example
Consider a component, say objCanvas1 which is having some text controls. I have included the ErrorTipManager to validate data and display errors if any.

Let us assume that the textcontrol have a minimum length of 5characters. When I typed 2 characters, it displays th errors to me.

On some action in objCanvas1, I use the removeChild method to remove objCanvas1 and I am adding another component objCanvas2.

In this case, I see the error message still getting displayed which should have not been.

I hope I am clear now, if not, please let me know, will clarify it

Anonymous said...

Thank you very much. Your solution was really helpful!


Tiago said...


There is a scroll issue when there is no tip error and you scroll it up or down.

How to reproduce:
1. insert some text in the text input (Scroll Box)
2. Click in another TextInput (and the tip error will disapear)
3. Scroll up or down (and the tip error will show again)

How to solve:
In the parentContainerScrolled method, where you have
"// re-position the error tip, also will make it visible if it was hidden"
you cannot force the hidden error tips to show [should be updateErrorTipPosition(target); ]

I don't know if this fix opens other issues, but it solves this problem.

Chris Callendar said...

Thanks a lot for your comment Tiago.

Your fix actually does introduce a new problem - that one line:
updateErrorTipPosition(target, true);
Hides the error tips when they are scrolled out of the visible viewport, and shows them when the input control is scrolled back into view.

The better solution is to change the ErrorTipManager.validHandler() function to call removeErrorTip(...) instead of hideErrorTip(...). This way whenever a component is valid the error tip is removed from the ErrorTipManager's cache. This is probably better anyway, even if it means the error tips will be created every time.


Brian J. Ackermann said...

Thanks for this!

I seem, however to be having trouble related to multiple validators on a single field.

I have an email field, and I'm using a standard emailvalidator, and a custom validator that works with my database remotely, to verify the uniqueness of the entered email.

When ever I just use one of the validators, your solution works very well, but when adding a second, then I get all sorts of unstable behavior.

Any thoughts ?

Thanks Again!

CashCow said...

Chris, this is great work, thanks for sharing it. I have been trying to use this in Flex 4 which work well except that is does not handle scrolling when inside a Scroller (as mentioned by digio above). This appears to be because the Scroller does not fire Move events like the scroll bars do in Flex 3.

I'm a novice Flex dev and the only way I could find to get this working was to listen for PropertyChangeEvents on the UIComponent and act on changes to horizontalScrollPosition or verticalScrollPosition. This is a bit messy so if anyone can suggest a better way of doing it I'm all ears.

I have used the registerValidatorOnPopUp(...) functions as these pass in the container component, in my case a Group wrapped in a Scroller. I extended the registerValidatorOnPopUp(...) function to register the event listener (and removed it in unregisterPopUpValidators(...)). I also filter out unwanted PCE's in targetMoved(...).

These changes seem to work most of the time but you get some 'odd' results when scrolling fast, as if it misses an event and then to tips can end up in the wrong place.

Code changes below:

public static function registerValidatorOnPopUp(validator:Validator, popUp:UIComponent, hideExistingErrorTips:Boolean = false):void
// add move/resize listeners on the popUp to keep the error tip positioned properly
popUp.addEventListener(MoveEvent.MOVE, targetMoved, false, 0, true);
popUp.addEventListener(ResizeEvent.RESIZE, targetMoved, false, 0, true);

// add propertyChange listener to detect scrolling in Flex4 Scroller
popUp.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, targetMoved, false, 0, true);


public static function unregisterPopUpValidators(popUp:UIComponent, validateExistingErrorTips:Boolean = false):void {
// remove the move/resize listeners on the popUp
popUp.removeEventListener(MoveEvent.MOVE, targetMoved);
popUp.removeEventListener(ResizeEvent.RESIZE, targetMoved);

popUp.removeEventListener(PropertyChangeEvent.PROPERTY_CHANGE, targetMoved);


private static function targetMoved(event:Event):void {
if (isExcludedEvent(event)) return;

// filter non scroll property events and duplicate scroll property events
static private function isExcludedEvent(event:Event):Boolean {
if (event.type == PropertyChangeEvent.PROPERTY_CHANGE && != "horizontalScrollPosition" && != "verticalScrollPosition" ) {
return true;
if (event.type == PropertyChangeEvent.PROPERTY_CHANGE && (event as PropertyChangeEvent).source != event.currentTarget) {
return true;
return false;

[winge] code formatting in these comments sucks!!![/winge]

Hope this can help some others.

Colin F. said...

Hi, I wanted to perform validateAll on the form after the user hits next and then prevent navigation away from the form if validation fails. I have modified the validateAll() method return a Boolean result - True if all validaiton passes, False otherwise. The code is below.

* Calls validate() on all the validators.
* Returns true if all validation passes, else false
public static function validateAll():Boolean {
var valid:Boolean = true;
// need to validator to figure out which error tips should be shown
for (var validator:Object in validators) {
var validationResult:ValidationResultEvent = validator.validate();
if (validationResult.results) {
valid = false;
return valid;

Hope this is useful to others too.
cheers, Colin

Chris Callendar said...

Thanks for contributing Colin!

metalikange said...

Hi Chris,

Thanks for this wonderful class !
I'm using it in a professional environment and it's so helpful.

As CashCow said, there is now a scroll bug using a parent scroller in Flex 4.1 . I didn't test CashCow code but can you confirm it's bug free or can you improved the class to make it usable?

Cheers, Raphi

Brian Bishop said...

Issue: Red Glow remains when input box clicked and valid input entered.

Fix: add the following tag to TextInputs - keyUp="txtAmount.drawFocus(true)"

This is a bug logged on Flex bug site

Robert Cesaric said...

I'm using this to validate a dynamically generated form and it works great:

var ti:TextInput = new TextInput();
var phoneValid:PhoneNumberValidator = new PhoneNumberValidator();
phoneValid.source = ti; = "text";
phoneValid.required = true;

Nice work!

Joshua Beall said...

This is a great tool! I'm having trouble using it with the CreditCardValidator class, though--the ErrorTips aren't visible unless you mouse over the corresponding fields.

I'm thinking this has something to do with the fact that CreditCardValidator is validating two separate fields, a "cardNumber" and a "cardType" field?

Is there a way to get the ErrorTipManager working with the CreditCardValidator component?


Joshua Beall said...

CashCow's fix didn't work for me, but got me started on a fix that *did* work for me. I posted about that here:

Sablotron said...

Hi Chris,

thanks a lot for sharing this. It is very useful.

I encountered an issue: if i replace the default validator messages inside the validator tag using the property e.g. requiredFieldError="some rather long Text", the tooltip size isn't correct. It seems to keep the size of the original text and the new text overlaps the tooltip. The text is coming from a localization class which works with standard validators.

Do you have an idea how to fix this?

Any help is greatly appreciated!


Chris Callendar said...

Hi Sab,

I don't know how to fix this problem. I spent a few minutes looking at it, but I didn't find a way to get the label displayed fully.

If you're interested I've put up a new blog post today on an alternate method displaying form validation errors.


Patrick Groves said...


There is one case where any error tips that are being displayed will not track their targets when those targets are repositioned. That is when the browser is resized. You can see this in the sample application if you wrap the components in say a VBox, eg.

<mx:VBox width="100%" height="100%" horizontalAlign="center" verticalAlign="middle">

. Sample application components.

If you run the application and resize the browser, the error tips will be misaligned.

To remidy this, simply add this code to ErrorTipManager.

import mx.core.Application;

* Adds an event listener for when the browser resizes.
private static var stageResize:* = Application.application.addEventListener(ResizeEvent.RESIZE, applicationResized, false, 0, true);

* When the browser is resized we need make the error tips track their targets.
private static function applicationResized(event:Event):void
for (var target:Object in errorTips) {
handleTargetMoved(target as DisplayObject);


Mark Cassar said...

Great class and thanks Patrick Groves for your update. Required that functionality as well as we noticed that limitation!

Anonymous said...

Thank you so much for this example!

One comment:
For Air application, there could be many windows. In order to show the error tip on the correct window, we need to add some parameter for createToolTip function. E.g.
errorTip = ToolTipManager.createToolTip(error, position.x, position.y, null, target.parentApplication);

Without the target.parentApplication, the error tip will be always shown on the root parent window.