You are currently browsing the category archive for the ‘Flex’ category.

Most XML data can be directly assigned to simple objects like strings and numbers, but when you have a more complex data type like a Date, you need to do a little parsing first.

Consider the following XML:

var response : XML =
    <response>
        <status date="2011-07-07">200</status>
    </response>;


I’ve covered how to get the data in the ‘status’ node before, but to get the data in the ‘date’ attribute and assign it to a Date object, you’d do the following in your code:

var dateString : String = response.status.@date;
var date : Date = DateFormatter.parseDateString (dateString);


Note that this will only work if the dateString is in an appropriate format, like “YYYY-MM-DD” or “YYYY-MM-DD HH:MM:SS”.

A while back I wrote a post on how to access XML using e4x, but I only covered how to get data directly from nodes. Well a few days ago I found myself needing to access a couple attributes from an XML response, so I thought I’d revisit the subject.

Consider the following XML:

var response : XML =
    <response>
        <status interesting="yes">200</status>
    </response>;


To get the data in the ‘status’ node and ‘interesting’ attribute, you’d do the following in your code:

var status : Number = response.status;
var interesting : String = response.status.@interesting;


Simple enough, right?

Now let’s add a namespace declaration to the XML:

var response : XML =
    <response xmlns:sample="http://userflex.wordpress.com/"&gt;
        <status interesting="yes">200</status>
    </response>;


To get at this data, you first need to retrieve the default namespace. Then you can use a similar scheme to get the node and attribute data:

var xmlns : Namespace;
var namespaces : Array = response.namespaceDeclarations ();

for each (var ns : Namespace in namespaces)
{
    if (ns.prefix == "")
    {
        xmlns = ns;
        break;
    }
}

var status : Number = response..xmlns::status;
var interesting : String = response..xmlns::status.@interesting;



Pro Tip: Let’s say you just want to check if an XML attribute has a particular value. You can accomplish that with this little bit of e4x magic:

var yesList : XMLList = response..*::status.(@interesting == "yes");
var isInteresting : Boolean = (yesList.length () > 0);

I ran into an issue a few days ago where the first label in my TabNavigator was being truncated when displayed within a popup window. It was baffling; the issue would only occur in a popup window. Further, if you hovered over the tab the full label would display, but the tab’s width wouldn’t change.

Needless to say, it was a trifle annoying. I finally found a solution here, which confirmed that it was a measuring issue in Flex when dealing with popups. While the solution at the aforementioned link worked, I noticed that it took a second for the change to register after the tabs were displayed. This means you would actually see the first label truncated for a moment before the tabs were remeasured.

Figuring I could do one better, I came up with my own custom component:

package
{
    import mx.containers.TabNavigator;
    import mx.controls.Button;
    import mx.events.FlexEvent;

    public class PopUpFriendlyTabNavigator extends TabNavigator
    {
        public function PopUpFriendlyTabNavigator ()
        {
            super ();

            addEventListener (FlexEvent.CREATION_COMPLETE,
                onCreationComplete);
        }

        private function onCreationComplete (event : FlexEvent) : void
        {
            var firstTab : Button = getTabAt (0);

            if (firstTab)
            {
                firstTab.invalidateDisplayList ();
                firstTab.validateNow ();
            }
        }

    }
}


Nothing fancy here. Just like the other solution, I created a custom TabNavigator and call invalidateDisplayList() on the first tab in a creationComplete event handler.

However in my solution I also immediately call validateNow() which eliminates the truncated label before the tabs are displayed. I also added some error handling (for good measure) to make sure the first tab exists.

This is a bit of code for a custom header renderer that’ll allow you to toggle ascending and descending sorts on a data grid.

Here’s the code for the renderer:

<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml&quot;
    horizontalScrollPolicy="off"
    verticalScrollPolicy="off"
    creationComplete="onCreationComplete()"
    click="onClick()">

    <mx:Script>
        <![CDATA[
            import mx.collections.ArrayCollection;
            import mx.collections.Sort;
            import mx.collections.SortField;
            import mx.controls.AdvancedDataGrid;

            [Embed(source="/css/assets/sort_arrow_up.png")]
            private static var UpSortArrow : Class;

            [Embed(source="/css/assets/sort_arrow_down.png")]
            private static var DownSortArrow : Class;

            private static var renderers : EventDispatcher =
                new EventDispatcher ();
            private var ignoreSortArrowUpdate : Boolean;

            private function onCreationComplete () : void
            {
                renderers.addEventListener ("sortArrowUpdated",
                    onSortArrowUpdated);

                // pick a column to sort by default
                if (data && data.dataField == "dataField")
                {
                    updateSortArrow (true);
                    sortColumn ();
                }
            }

            private function onClick () : void
            {
                updateSortArrow (true);
                ignoreSortArrowUpdate = true;

                renderers.dispatchEvent
                    (new Event ("sortArrowUpdated"));

                sortColumn ();
            }

            private function onSortArrowUpdated (event : Event) : void
            {
                if (! ignoreSortArrowUpdate)
                {
                    updateSortArrow (false);
                }

                ignoreSortArrowUpdate = false;
            }

            private function updateSortArrow (selected : Boolean)
                : void
            {
                sortArrow.source = (selected ? (sortArrow.source ==
                    DownSortArrow ? UpSortArrow : DownSortArrow)
                    : null);
            }

            private function sortColumn () : void
            {
                var dataProvider : ArrayCollection =
                    (this.owner as AdvancedDataGrid).dataProvider as
                    ArrayCollection;

                dataProvider.sort = new Sort ();
                dataProvider.sort.fields = [new SortField
                    (data.dataField, false, sortDescending)];

                dataProvider.refresh ();
            }

            private function get sortDescending () : Boolean
            {
                return (sortArrow.source == DownSortArrow);
            }

        ]]>
    </mx:Script>

    <mx:Text width="100%"
        text="{data.headerText}"
        textAlign="center"
        selectable="false" />

    <mx:Image id="sortArrow"
        right="0"
        bottom="0" />

</mx:Canvas>


When you click on a column header, not only will it toggle the appropriate sort, it will update the sort icon, and dispatch an event so the other columns know to hide their sort icons.

Finally, here’s how to set up your data grid to use the custom renderer:

<mx:AdvancedDataGrid id="grid"
    sortableColumns="false"
    headerRenderer="com.company.app.view.ToggleSortHeaderRenderer" />


It may seem counterintuitive to set sortableColumns to false on the data grid, but you don’t want Flex messing things up by also trying to sort the columns. Let the custom renderer do all the heavy lifting here.

You’ll also need to specify the sort icons (up and down arrows) and set the column that you’d like to be sorted by default.

When making an asynchronous data call to populate a data grid, it’s nice to be able to display something while the data is being retrieved.

The following sample code will display a simple “Loading…” message:

<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml&quot;
    xmlns:component="com.company.app.view.*"
    width="100%"
    height="100%">

    <mx:Script>
        <![CDATA[

            // instance variables
            [Bindable] public var loadingData : Boolean;

        ]]>
    </mx:Script>

    <mx:AdvancedDataGrid id="grid"
        width="100%"
        height="100%" />

    <component:LoadingScreen y="{grid.headerHeight + 1}"
        width="100%"
        height="100%"
        visible="{loadingData}" />

</mx:Canvas>


Here’s the LoadingScreen component:

<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml&quot;
    horizontalAlign="center"
    verticalAlign="middle">

    <mx:Label text="Loading…" />

</mx:VBox>


All you need to do is set the loadingData property to true when you make the data call, and set it back to false after the data has been retrieved. In this example, the column headers are always displayed, so the transition is subtle.

For a smoother transition, you can also add a Fade effect to the LoadingScreen component.

Follow

Get every new post delivered to your Inbox.