views:

107

answers:

3

Hello there,

Brief details: I'm using Flex 3.5.

I have a Tree component that's used as a navigation menu between different 'pages'. When the user clicks a certain option in the menu, I switch the 'page' by switching between State components in my application. The thing is that when the user indeed clicks an option in the menu, I want to perform a validation of some of the information in a certain component. If the validation fails, I show an alert, and I'd like to prevent the navigation to the other page. One part of this is simply not changing the currentState of the document, but the tree component still goes on with the change event, and the result is page A still being shown on the screen, whereas the selected option in the tree is page B (to which the user wanted to navigate, but failed since some of the information wasn't valid).

I tried to figure out how I can cancel the change event on the tree component itself. The thoughts I had didn't quite fit nicely:

I searched for a slightly different event (such as 'changing' or 'startChange') on which I can call the stopPropagation() method (since the regular 'change' event is not cancelable), but none exists for the Tree component.

I also thought about always saving the current option that's selected in the Tree component by myself, and when the validation fails, I will set the Tree's selectedItem to that saved option. That's also ugly because such an action will raise another change event on the Tree, thus another change to the States components, and another population of the page in which I'm already at. That's something I really don't want to do.

I also though about using a different component, such as Menu (and I also found an implementation of a vertical Menu), but that doesn't even seem to help. The same problem will exist there.

Is there a proper way to do this? There must be a best-practice for preventing a change process to commit!

Many thanks in advance, Daniel

A: 
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:local="*">

    <mx:Script>
    <![CDATA[
        import mx.controls.Alert;
        import mx.events.ListEvent;

        private function tree_changeHandler(event:ListEvent):void
        {
            trace("Change, selectedItem.label is: " + tree.selectedItem.label);
        }

        protected function tree_itemClickHandler(event:ListEvent):void
        {
            var data:Object = event.itemRenderer.data;
            if (!tree.isItemSelectable(data))
                Alert.show("Item \"" + data.label + "\" is not selectable");
        }

    ]]>
    </mx:Script>

    <local:MyTree id="tree" change="tree_changeHandler(event)" itemClick="tree_itemClickHandler(event)">
        <local:dataProvider>
            <mx:ArrayCollection>
                <mx:Object label="Label 1"/>
                <mx:Object label="Label 2"/>
                <mx:Object label="Label 3 (non-selectable)"/>
                <mx:Object label="Label 4"/>
            </mx:ArrayCollection>
        </local:dataProvider>
    </local:MyTree>

</mx:Application>

Source for MyTree.as:

package
{
import mx.controls.Tree;

public class MyTree extends Tree
{

    override public function isItemSelectable(data:Object):Boolean
    {
        if (!super.isItemSelectable(data))
            return false;

        var label:String = data.label;
        if (label.indexOf("non-selectable") >= 0)
            return false;

        return true;
    }

}
}
Maxim Kachurovskiy
A: 

Thanks a lot for the answer,

I couldn't quite adjust it to my situation, though.

In your scenario, each item in the tree already "knows" whether it's selectable. In my case, the decision of whether an item is selectable or not has to be done when the user actually clicks the item. That's when the information has to be validated. I tried to apply your approach to my situation by using a custom Tree implementation, and by checking in the isItemSelectable() method for a property in each item indicating whether it's selectable or not. I tried to dynamically determine the value of this property for each item during the change process. I tried to do it in the 'change' and 'itemClick' events, but both occur after the isItemSelectable() method is invoked (kind of obvious...) That's why there's no place for me to put the code that determines the selectability of each item before the isItemSelectable() method is called.

More ideas are welcome.

Thanks again, Daniel

Daniel
A: 

Eventually I found the place to put the code that determines each item's selectability: when the information that should be validated is changed, I perform the validation, and according to its result I set a property to all of the items in the Tree component, indicating whether they can be navigated to or not. If the validation was successful, the property is set to allow navigation, and if unsuccessful, it is set not to allow navigation.

Like Maxim, I extend the Tree component and overrode the isItemSelectable() method to check this property of the specified item, this way preventing the change process.

The access between the view that holds the information to-be-validated, and the view that holds the Tree component (they are not necessarily the same view) is done via a presentor class that holds both views (I use the MVP mechanism). This is not the most elegant design, but it is much better than anything else I could have thought of. The alleged problem with the design is the coupling between the views and the complexity of the presentor, that has to deal with more than one view and have methods that are related to the interaction between the views (instead of methods that represent actions of a specific view). The thing is that business-wise, the two views are coupled (since the information in one affects the navigation tree in the other), thus the presentor couples between them. The coupling is also done through the interface of the presentor, so that each view doesn't really "know" the other view.

I hope it might help other people.

Thanks, Daniel

Daniel