I recently came across an interesting problem at work with a TabNavigator. The problem we had is that when a user clicked on a tab, we needed to validate some data on the client-side. If the data was valid, then the TabNavigator could proceed with changing tabs. Otherwise, if the data was invalid, an Alert error message would be displayed and the TabNavigator would not switch away from the current tab.

There were two solutions that came to my mind.  One way would be to override the set selectedIndex function in TabNavigator.  The other way would be to intercept an “change” event dispatched by the TabNavigator, perform the validation, and then if necessary prevent the TabNavigator from changing.

In the end I decided to go with the second option because I found it to be a cleaner implementation.  This relied on using the “onCapture” property of addEventListener so that I could catch the event as soon as possible (during the capture phase), and prevent it from reaching the TabNavigator (during the target phase).  To do this, I included this line of code in the creationComplete function for my component.

?View Code ACTIONSCRIPT
1
tabNavigator.addEventListener(MouseEvent.CLICK, onTabClick, true);

The third parameter simply means that this is a capture event instead of a traditional bubble event.  The difference is explained here.  However, it is important to keep in mind that if I needed to remove this event listener, I would need to mark true on the third parameter of removeEventListener like so:

?View Code ACTIONSCRIPT
1
tabNavigator.removeEventListener(MouseEvent.CLICK, onTabClick, true);

Otherwise, the capture event handler will not be removed if I leave it off.

You may notice that I made the event listener listen to the “click” event rather than the tab “change” event.  This kind of threw me off at first.  When I listened to the “change” event, the TabNavigator would still continue to change tabs even if I tried to stop it.  This occurs because the “click” event is the event that gets the ball rolling with tab changes initially.  The “change” event is dispatched as an after-thought to let the developer know that the change has occurred.  My event listener looks kind of like this:

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
private function onTabClick(event:MouseEvent):void 
{
    if (event.target.parent is TabBar) 
    {
        var selectedIndex:uint= TabBar(event.target.parent).getChildIndex(event.target as DisplayObject);
        var isValid:Boolean = validateUserInfo();
 
        if (!isValid) 
        {
            event.stopImmediatePropagation();
            event.preventDefault();
        }
    }
}

Now to explain the reasoning behind this… The target that is invoked when a tab is clicked on is a Tab display object.  The class Tab is an internal Flex class and not for external use.  So, instead I used the Tab’s parent which is TabBar to perform a class check to make sure the we are only handling the event when a Tab is clicked, and not any other part of the TabNavigator.

Next, for our project, we needed to know what the selectedIndex would have been should the Tab have changed.  Since the TabNavigator has not been able to process the Tab change, we cannot access the selectedIndex property because it will just reflect the current tab we are on.  So, to simulate this, I casted the Tab’s parent to a TabBar and then grabbed the child index of the target (the tab that was clicked) by using the TabBar’s getChildIndex function (inherited from the Container class).

After that, I went about validating the form in another function.  This function returns a Boolean value to indicate whether the form is valid or not.  If the form is not valid, I prevent the Tab from changing by calling event.stopImmediatePropogation() to stop the event from trickling down to the TabNavigator, and I also call event.preventDefault() just in case the event happened to continue down to the TabNavigator for some strange reason (I don’t think this would ever happen, but I am just paranoid sometimes!).

I found that these steps did the trick for me and I was able to not go through the hassle of override TabNavigator and manually coercing Flex to do what I wanted.  Hope this helps!

UPDATE: If you are looking for a good solution to prevent a TabBar from changing instead of the TabNavigator, check out Aaron Hardy’s blog. He used it in a project we did together and it worked great.