The complete guide to MXML in the Feathers SDK

The Feathers SDK supports using MXML to declaratively layout user interfaces at compile time. MXML provides a number of features to reduce boilerplate ActionScript code and make user interface code more readable.

Add children to containers

With only a quick glance at MXML code, we can easily recognize the relationship between a component and its parent container. Adding a child to a container is as simple as nesting an XML element inside another.

<f:LayoutGroup>
    <f:Slider/>
</f:LayoutGroup>

Set properties on objects

The properties of an object created in MXML may be set in one of two ways. First, we can set properties using XML attributes:

<f:Slider minimum="0" maximum="100" value="10"/>

Simple types like Number, int, uint, String, and Boolean can be set this way.

Alternatively, we can pass more complex values to a property by referencing it using a child XML element:

<f:LayoutGroup>
    <f:layout>
        <f:HorizontalLayout padding="10"/>
    </f:layout>

    <f:Slider minimum="0" maximum="100" value="10"/>
</f:LayoutGroup>

Here, we create a new instance of HorizontalLayout, set one of its properties, and then we pass it to the layout property.

Add event listeners

Similar to setting properties, we can listen for events by referencing the event type as an XML attribute:

<f:Slider minimum="0" maximum="100" value="10"
    change="slider_changeHandler(event)"/>

We've added an event listener for Event.CHANGE to the Slider. In the next section, we'll learn how to write the ActionScript code for this event listener.

Include ActionScript code inside an MXML class

In the previous example, we listened for an event. Let's create the event listener function using ActionScript. We can add an <fx:Script> block to our MXML component to include ActionScript code:

<fx:Script><![CDATA[

    private function button_triggeredHandler(event:Event):void
    {
        trace( "slider value changed!" );
    }

]]></fx:Script>

Reference MXML objects in ActionScript

If we want to access the value property of the Slider in our event listener, we can give the Slider an id.

<f:Slider id="slider"
    minimum="0" maximum="100" value="10"
    change="slider_changeHandler(event)"/>

Now, we can reference the Slider that we created in MXML like a member variable on an ActionScript class:

private function button_triggeredHandler(event:Event):void
{
    trace( "slider value changed! " + this.slider.value );
}

Bind data to properties

Data binding can save us time by skipping the boilerplate code for setting up event listeners and setting properties.

<f:LayoutGroup>
    <f:layout>
        <f:HorizontalLayout/>
    </f:layout>

    <f:Slider id="slider" minimum="0" maximum="100" value="10"/>
    <f:Label text="{slider.value}"/>
</f:LayoutGroup>

Now, when the slider's value property changes, we display the value in a label.

Built-in primitive types

A number of primitive data types that we use frequently in ActionScript may also be used to create objects in MXML. For instance, the Number, int, and uint classes may define numeric values.

<fx:Number>32</fx:Number>
<fx:Number>123.45</fx:Number>
<fx:int>-650</fx:int>
<fx:uint>17925</fx:uint>

The Boolean class may be true or false:

<fx:Boolean>true</fx:Boolean>
<fx:Boolean>false</fx:Boolean>

The String class represents text, including support for <![CDATA[ ]> to allow unescaped text:

<fx:String>Hello World</fx:String>
<fx:String><![CDATA[Complex strings using <XML> & more!]]></fx:Boolean>

The Object class may define sets of key-value pairs using both attributes and child elements:

<fx:Object numeric="35">
    <fx:text>
        <fx:String>Message for you, friend!</fx:String>
    </fx:text>
</fx:Object>

Additionally, Array objects can be populated by adding multiple children:

<fx:Array>
    <fx:Number>1</fx:Number>
    <fx:Number>2</fx:Number>
    <fx:Number>3</fx:Number>
</fx:Array>

Similarly, the Vector class defines a typed array:

<fx:Vector type="starling.display.DisplayObject">
    <fx:Button label="Back"/>
    <fx:Button label="Settings"/>
</fx:Vector>

Define properties on an MXML class

An <fx:Declarations> element may be used in an MXML class to create MXML objects that aren't user interface components.

<fx:Declarations>
    <fx:Array id="items">
        <fx:Object label="Bread"/>
        <fx:Object label="Eggs"/>
        <fx:Object label="Milk"/>
    </fx:Array>
</fx:Declarations>

Give the object an id, and it may be referenced in MXML for things like binding, and inside an ActionScript <fx:Script> element.

Define metadata on an MXML class

Metadata may be added to an MXML class using a <fx:Metadata> element. For instance, you might define a component's with metadata:

<fx:Metadata>
    [Event(name="change",type="starling.events.Event")]
</fx:Metadata>

Create inline sub-component factories

When sub-components require a factory, we can define it in the MXML using <fx:Component> instead of writing the factory as a function in ActionScript code:

<f:List>
    <f:itemRendererFactory>
        <fx:Component>
            <f:DefaultListItemRenderer labelField="text"/>
        </fx:Component>
    </f:itemRendererFactory>
    <f:dataProvider>
        <f:ListCollection>
            <fx:Object text="Milk"/>
            <fx:Object text="Eggs"/>
            <fx:Object text="Flour"/>
            <fx:Object text="Sugar"/>
        </f:ListCollection>
    </f:dataProvider>
</f:List>

In the MXML above, we create a DefaultListItemRenderer and set its labelField as an inline component.

<fx:Component> versus factoryFromInstance()

When using <fx:Component>, we may need to call functions defined in the main <fx:Script> element in the MXML file. Because <fx:Component> is considered a different class, we need to use the implicit outerDocument property to access the main scope of the MXML file.

In the following example, we have a PanelScreen with a Button in its header. When the button is triggered, we want to call a function defined outside of the <fx:Component> element. This requires outerDocument:

<f:PanelScreen xmlns:fx="http://ns.adobe.com/mxml/2009"
    xmlns:f="library://ns.feathersui.com/mxml">
    <f:headerFactory>
        <fx:Component>
            <f:Header>
                <f:leftItems>
                    <fx:Vector type="starling.display.DisplayObject">
                        <f:Button label="Back"
                            triggered="outerDocument.goBack()"/>
                    </fx:Vector>
                </f:leftItems>
            </f:Header>
        </fx:Component>
    </f:headerFactory>
    <fx:Script>
    <![CDATA[
        public function goBack():void
        {
            this.dispatchEventWith( Event.COMPLETE );
        }
    ]]>
    </fx:Script>
</f:PanelScreen>

When using outerDocument, we only have access to public APIs. Trying to call a private or protected function will result in an error.

Perhaps we'd prefer to make that goBack() function private. We can do that by using factoryFromInstance() instead of <fx:Component>.

<f:PanelScreen xmlns:fx="http://ns.adobe.com/mxml/2009"
    xmlns:f="library://ns.feathersui.com/mxml"
    headerFactory="{factoryFromInstance(customHeader)}">
    <fx:Declarations>
        <f:Header id="customHeader">
            <f:leftItems>
                <fx:Vector type="starling.display.DisplayObject">
                    <f:Button label="Back"
                        triggered="goBack()"/>
                </fx:Vector>
            </f:leftItems>
        </f:Header>
    </fx:Declarations>
    <fx:Script>
    <![CDATA[
        private function goBack():void
        {
            this.dispatchEventWith( Event.COMPLETE );
        }
    ]]>
    </fx:Script>
</f:PanelScreen>

We can instantiate the Header component inside an <fx:Declarations> element. This will create the instance, but it will not be added to the display list. We give it an id so that it is assigned to a variable.

Then, when we set the headerFactory property, we pass customHeader to factoryFromInstance(), and it will automatically create a factory that returns the Header instance that we created in <fx:Declarations>.

Since the Header is defined inside the scope of our MXML file, we don't need to use outerDocument. We can call a private function directly.