by Adobe

adobe_logo_bio

Created

22 March 2010

Requirements
User level
All
In typical applications built with Adobe Flex, you do not code the entire application in a single source code file. Such an implementation makes it difficult for multiple developers to work on the project simultaneously, makes it difficult to debug, and discourages code reuse.
 
Instead, you develop applications by using multiple MXML and ActionScript files. This architecture promotes a modular design, code reuse, and lets multiple developers contribute to the implementation.
 
MXML components are MXML files that you reference by using MXML tags from within other MXML files. One of the main uses of MXML components is to extend the functionality of an existing Flex component.
 
For example, Flex supplies a ComboBox control that you can use as part of a form that collects address information from a customer. You can use a ComboBox to let the user select the Country portion of the address from a list of countries in the world. In an application that has multiple locations where a user can enter an address, it would be tedious to create and initialize multiple ComboBox controls with the information about all the countries in the world.
 
Flex follows the same principle; you create an MXML component that contains a ComboBox control with all country names defined within it. Then, wherever you must add a country selector to your application, you use your custom MXML component.
 
This article covers:
 
Note: You can also create Flex components in ActionScript. For more information, see Building components in ActionScript.
 

 
Creating a simple MXML component

You create an MXML component in an MXML file where the component's filename becomes its MXML tag name. For example, a file named CountryComboBoxSimpleMXML.mxml defines a component with the tag name of <CountryComboBoxSimpleMXML>.
 
The root tag of an MXML component is a component tag, either a Flex component or another MXML component. The root tag specifies the following namespace:
 
  • xmlns:fx="http://ns.adobe.com/mxml/2009"
  • xmlns:s="library://ns.adobe.com/flex/spark"
  • xmlns:mx="library://ns.adobe.com/flex/mx"
For example, the following MXML component extends the standard Flex ComboBox control. You can place custom components either in the root folder of your project or in a subfolder. Adobe recommends the latter location as a best practice. In this example, the custom component is placed in a folder called components. In the main application MXML file, you map this folder to a namespace called custom and use the fully-qualified tag name of <custom:CountryComboBoxSimpleMXML> to refer to the component.
 
Tip: In real-world applications, you may see custom components placed in a folder structure that uses a reverse domain name structure (e.g., xmlns:custom="com.adobe.quickstarts.customcomponents.*"). This convention avoids namespace conflicts between identically named components by different vendors. For example, two component libraries may each have a Map component that you want to use in your application. If one is in the com/vendorA/ folder and the other is in the com/vendorB/ folders, they do not conflict.
 
 
Example
components/CountryComboBoxSimpleMXML.mxml
 
<?xml version="1.0" encoding="utf-8"?> <!-- components/CountryComboBoxSimpleMXML.mxml --> <s:ComboBox xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx"> <s:dataProvider> <s:ArrayList> <fx:String>United States</fx:String> <fx:String>United Kingdom</fx:String> <!-- Add all other countries... --> </s:ArrayList> </s:dataProvider> </s:ComboBox>
Main application MXML file
 
<?xml version="1.0" encoding="utf-8"?> <!-- MxmlComponentSimple.mxml --> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:custom="components.*" width="220" height="115"> <custom:CountryComboBoxSimpleMXML selectedIndex="0"/> </s:Application>

 
Referencing properties and methods of a custom component

The CountryComboBoxSimpleMXML.mxml file specifies the ComboBox control as its root tag, so you can reference all of the properties and methods of the ComboBox control within the MXML tag of your custom component, or in the ActionScript specified in an <fx:Script> tag. For example, the following example specifies a listener for the close event for your custom control.
 
Note: MXML files in Flex get compiled down into ActionScript classes before being converted into SWF bytecode that is run by the Adobe Flash Player. When you specify a root tag for a custom MXML component, you are actually having your component's class extend the class of the root component. This is why your custom component inherits the methods and properties of the root tag.
 
In the following example, the CountryComboBox MXML file gets compiled down into a class. The Flex naming convention is to use an initial cap on the component name, since it corresponds to a class name.
 
 
Example
components/CountryComboBox.mxml
 
<?xml version="1.0" encoding="utf-8"?> <!-- components/CountryComboBoxSimpleMXML.mxml --> <s:ComboBox xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx"> <s:dataProvider> <s:ArrayList> <fx:String>United States</fx:String> <fx:String>United Kingdom</fx:String> <!-- Add all other countries... --> </s:ArrayList> </s:dataProvider> </s:ComboBox>
Main application MXML file
 
<?xml version="1.0" encoding="utf-8"?> <!-- MxmlComponentInheritence.mxml --> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:custom="components.*" width="270" height="170"> <fx:Script> <![CDATA[ import flash.events.Event; private function handleCloseEvent(eventObj:Event):void { status.text = "You selected: \r" + countries.selectedItem as String; } ]]> </fx:Script> <s:Panel title="Custom component inheritence"> <s:layout> <s:VerticalLayout paddingLeft="10" paddingRight="10" paddingTop="10" paddingBottom="10"/> </s:layout> <custom:CountryComboBoxSimpleMXML id="countries" close="handleCloseEvent(event);"/> <s:Label id="status" text="Please select a country from the list above." width="136"/> </s:Panel> </s:Application>

 
Creating composite MXML components

A composite MXML component contains multiple component definitions within it. To create a composite component, you specify a container as its root tag, and then add Flex components as children of the container.
 
The AddressForm component in the following example contains an address form created by specifying a Form container as the root tag of the component, and then defining several children of the Form container, including another custom component, the CountryComboBoxSimpleMXML.
 
 
Example
components/AddressForm.mxml
 
<?xml version="1.0" encoding="utf-8"?> <!-- components/AddressForm.mxml --> <mx:Form xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:custom="components.*"> <mx:FormItem label="Name"> <s:TextInput/> </mx:FormItem> <mx:FormItem label="Street"> <s:TextInput/> </mx:FormItem> <mx:FormItem label="City"> <s:TextInput/> </mx:FormItem> <mx:FormItem label="State/County"> <s:TextInput/> </mx:FormItem> <mx:FormItem label="Country"> <custom:CountryComboBoxSimpleMXML/> </mx:FormItem> </mx:Form>
components/CountryComboBoxSimpleMXML.mxml
 
<?xml version="1.0" encoding="utf-8"?> <!-- components/CountryComboBoxSimpleMXML.mxml --> <s:ComboBox xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx"> <s:dataProvider> <s:ArrayList> <fx:String>United States</fx:String> <fx:String>United Kingdom</fx:String> <!-- Add all other countries... --> </s:ArrayList> </s:dataProvider> </s:ComboBox>
Main application MXML file
 
<?xml version="1.0" encoding="utf-8"?> <!-- MxmlComponentComposite.mxml --> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:custom="components.*" width="400" height="290"> <s:Panel title="Composite component"> <s:layout> <s:VerticalLayout paddingLeft="10" paddingRight="10" paddingTop="10" paddingBottom="10"/> </s:layout> <custom:AddressForm/> </s:Panel> </s:Application>
If you include child tags of the root container tag in an MXML component file, you cannot add child tags when you use the component as a custom tag in another MXML component file. If you define an empty container in an MXML file, you can add child tags when you use the component as a custom tag. For example, the following use of the AddressForm is incorrect and does not compile:
 
<!-- Incorrect --> <custom:AddressForm> <mx:FormItem label="Post code"> <mx:TextInput/> </mx:FormItem> </custom:AddressForm>
If you must extend the functionality of a composite component, create a custom component that uses the composite component as its root tag.
 
Note: The restriction on child tags refers to the child tags that correspond to visual components. Visual components are subclasses of the UIComponent component. You can always insert tags for nonvisual components, such as ActionScript blocks, styles, effects, formatters, validators, and other types of nonvisual components, regardless of how you define your custom component.
 

 
Creating reusable MXML components

One of the common goals when you create MXML components is to create configurable and reusable components. For example, you might want to create MXML components that take properties, dispatch events, define new style properties, have custom skins, or use other customizations.
 
One design consideration when you create custom MXML components is reusability. That is, do you create a component that is tightly coupled to your application, or create one that is reusable in multiple applications?
 
A tightly coupled component is written for a specific application, often by making it dependent on the application's structure, variable names, or other details. If you change the application, you might have to modify a tightly coupled component to reflect that change. A tightly coupled component is often difficult to use in another application without rewriting it.
 
You design a loosely coupled component for reuse. A loosely coupled component has a well-defined interface that specifies how to pass information to the component, and how the component passes back results to the application.
 
You typically define the properties of a loosely coupled component to pass information to it. These properties, defined by using implicit accessors (setter and getter methods), specify the data type of the parameter value. In the following example, the CountryComboBox custom component defines a public useShortNames property that exposes the _useShortNames private property using the get useShortNames() and set useShortNames() accessor methods.
 
The [Inspectable] metadata tag for the _useShortNames private property defines an attribute that appears in the attribute hints and Tag inspector in Adobe Flash Builder. You can also use the metadata tag to limit the allowable values of the property.
 
Note: All public properties show up in MXML code hints and in the Property Inspector. If you have extra information about the property that will help code hints or the Property Inspector (such as enumeration values or that a String is actually a file path) then also add [Inspectable] metadata with that extra info.
 
MXML code hints and the Property Inspector properties all come from the same data, so if it shows up in one it should always show up in the other. ActionScript code hints, on the other hand, do not require metadata to work correctly so you will always see the appropriate code hints in ActionScript depending on what scope you are in. Flash Builder uses identifiers (such as public, protected, private, and static) plus the current scope to figure out what ActionScript code hints to show.
 
The best practice for defining components that return information back to the main application is to design the component to dispatch an event that contains the return data. In that way, the main application can define an event listener to handle the event and take the appropriate action. You also use events in data binding. The following example uses the Bindable metadata tag to make useShortNames a bindable property. The implicit setter for the useShortNames property dispatches the change event that is used internally by the Flex framework to make data binding work.
 
 
Example
components/CountryComboBoxMXML.mxml
 
<?xml version="1.0" encoding="utf-8"?> <!-- components/CountryComboBoxMXML.mxml --> <s:ComboBox xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx"> <fx:Script> <![CDATA[ import mx.collections.ArrayList; private var countryArrayShort:ArrayList = new ArrayList(["US", "UK"]); private var countryArrayLong:ArrayList = new ArrayList(["United States", "United Kingdom"]); // Determines display state. The inspectable metadata tag // is used by Flash Builder. [Inspectable(defaultValue=true)] private var _useShortNames:Boolean = true; // Implicit setter public function set useShortNames (state:Boolean):void { // Call method to set the dataProvider based on the name length. _useShortNames = state; if (state) { this.dataProvider = countryArrayShort; } else { this.dataProvider = countryArrayLong; } // Dispatch an event when the value changes // (used in data binding.) dispatchEvent(new Event("changeUseShortNames")); } // Allow other components to bind to this property [Bindable(event="changeUseShortNames")] public function get useShortNames():Boolean { return _useShortNames; } ]]> </fx:Script> </s:ComboBox>
Main application MXML file
 
<?xml version="1.0" encoding="utf-8"?> <!-- MxmlComponentAdvanced.mxml --> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:custom="components.*" width="260" height="200"> <s:Panel title="Advanced custom components"> <s:layout> <s:VerticalLayout paddingLeft="10" paddingRight="10" paddingTop="10" paddingBottom="10"/> </s:layout> <!-- Set a custom property on a custom component --> <custom:CountryComboBoxMXML id="countries" useShortNames="false"/> <!-- Use data binding to display the latest state of the property. --> <s:Label text="useShortNames = {countries.useShortNames}"/> <s:controlBarContent> <s:Group> <s:Button label="Toggle Display" left="75" click="countries.useShortNames = !countries.useShortNames;"/> </s:Group> </s:controlBarContent> </s:Panel> </s:Application>

 
For more information