Wrox Home  
Search
Professional Adobe Flex 2
by Rich Tretola, Simon Barber, Renaun Erickson
May 2007, Paperback


Excerpted from Professional Adobe Flex 2

CREATING ADOBE FLEX 2 MXML COMPONENTS

by Rich Tretola

Creating components is one of the most important features of Flex because it allows for the reuse of code created either by you or by other developers. Once a component is created, it can be reused by simply including it within your application. This article shows you how to create simple and advanced components. You also learn how to scope and style your components.

Creating Simple MXML Components

To create a component, you simply create a new MXML file and add a visual component as the root tag. Ensure that you add the mx namespace to the root tag. If you are using Flex Builder, you can accomplish this by selecting File -> New -> MXML Component. Simply give it a unique name and choose a Based On tag and you should end up with something like this:

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="300">
    
</mx:Canvas>

Now, add anything you want to the custom component and use it within your application. To use a custom component, you will need to add it as an xml namespace in the parent's root tag. Assuming the component created is named Test.mxml and is in a folder named components, this is what the main application file would look like when the component is added:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
 xmlns:comps="components.*">
    <comps:Test id="myComponent"/>
</mx:Application>

Take a look at a real-world example of where you may want to use a very simple custom component. Imagine that you are building an application where the user needs to select his or her birthday and you want the user to choose the month from a ComboBox rather than allowing him or her to free-type the value (which will result in inconsistent data). You could add a ComboBox to your application and create an array of months as the dataProvider, or you could create a very simple custom component that could then be reused within the same application or any application you will build in the future. Selecting the ComboBox to base the component on is the same as extending ComboBox. It just makes it easier to extend a ComboBox, rather than creating a composite component with a ComboBox and its data wrapped in a Canvas. The component would look something like that shown in Listing 1.

Listing 1: SimpleMonths.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:ComboBox xmlns:mx="http://www.adobe.com/2006/mxml">
  <mx:dataProvider>
    <mx:ArrayCollection>
      <mx:String>January</mx:String>
      <mx:String>February</mx:String>
      <mx:String>March</mx:String>
      <mx:String>April</mx:String>
      <mx:String>May</mx:String>
      <mx:String>June</mx:String>
      <mx:String>July</mx:String>
      <mx:String>August</mx:String>
      <mx:String>September</mx:String>
      <mx:String>October</mx:String>
      <mx:String>November</mx:String>
      <mx:String>December</mx:String>
    </mx:ArrayCollection>
  </mx:dataProvider>
</mx:ComboBox>

Listing 2 shows the new component being included in the application.

Listing 2: SimpleMonthsMain.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
    xmlns:comps="*" backgroundColor="#FFFFFF">
  <mx:Panel title="Simple Months" width="100%" height="100%" 
    paddingTop="10" paddingLeft="10" paddingRight="10" paddingBottom="10" 
    layout="horizontal">
    <comps:SimpleMonths />
  </mx:Panel>
</mx:Application>

Figure 1 shows the SimpleMonths component.

Professional Adobe Flex 2 : Figure 1
Figure 1: The SimpleMonths component

Scoping Your Components

Scoping within a Flex application refers to the value that will be returned from the this keyword. When this is accessed from within the application's main file, it will be returned as the reference that Flex has assigned to mx.core.Application.application. When this is accessed from any Flex component, it will be returned as the full path from the root of your application to the component. In other words, this will always refer to the file you are working within. Take a look at Listing 3 and Listing 4.

Listing 3: Index.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
 backgroundColor="#FFFFFF" width="350" height="150"
 xmlns:comps="*">
    <mx:Label text="'this = ' {this}" />
    <comps:MyHBox id="myHBox"/>
</mx:Application>
Listing 4: MyHBox.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml">
  <mx:Label text="'MyHBox this = ' {this}" />
</mx:HBox>

In these examples, this is being accessed from the Index.mxml file (Listing 3) within the Label component, and this is also being accessed within the Label component of the MyHBox custom component (Listing 4). The results can be seen in Figure 2. Notice that when this is called from the custom component, the ID of the component is returned. If there is no ID supplied, Flex assigns an ID.

Professional Adobe Flex 2 : Figure 2
Figure 2: The this being accessed in multiple ways

Styling Your Components

Components are self-contained and can be styled individually, or they can simply accept the style properties defined by the parent application's style settings. Components can be styled inline by adjusting the style properties within the MXML code. They can be styled using CSS within an <mx:Style> tag, or they can be skinned. Listing 5 shows SimpleStyledMonths.mxml. In the example shown in Figure 3, the properties of the MXML root tag were updated to take on a purple style and square corners. Figure 3 shows the original custom component, as well as the styled version.

Listing 5: SimpleStyledMonths.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:ComboBox xmlns:mx="http://www.adobe.com/2006/mxml" 
   themeColor="#400080" borderColor="#800080"
   fillColors="[#800080, #ffffff]" 
   fillAlphas="[0.5, 0.5]" 
   cornerRadius="0">
  <mx:dataProvider>
    <mx:ArrayCollection>
      <mx:String>January</mx:String>
      <mx:String>February</mx:String>
      <mx:String>March</mx:String>
      <mx:String>April</mx:String>
      <mx:String>May</mx:String>
      <mx:String>June</mx:String>
      <mx:String>July</mx:String>
      <mx:String>August</mx:String>
      <mx:String>September</mx:String>
      <mx:String>October</mx:String>
      <mx:String>November</mx:String>
      <mx:String>December</mx:String>
    </mx:ArrayCollection>
  </mx:dataProvider>
</mx:ComboBox>

Professional Adobe Flex 2 : Figure 3
Figure 3: SimpleMonths and SimpleStyledMonths components

Advanced MXML Components

The SimpleMonths example was a very basic implementation of a custom component. More advanced components include custom properties, methods, and even additional components combined together to create what is known as a composite component. (Composite components are covered in chapter 12 "Creating MXML Components" of the book Professional Adobe Flex 2 (Wrox, 2007, ISBN: 978-0-470-10267-1)).

Adding Custom Properties and Methods to a Component

The SimpleMonths example created previously is limited to showing only the name of the month. But what if the application calls for the display to show only the number or the abbreviation of the months? Should you then create new components called MonthsByName, MonthsByNumber, and MonthsByAbr? he answer is no, because you can very easily build this functionality into one simple component. This can be accomplished by adding properties to your component, which can be set when the component is included in the parent document.

The example shown in Listing 6 adds a property named labelFieldVar to determine what value to show within the ComboBox. The values that can be set are full, abr, and num, which will display the full month name, the month's three-letter abbreviation, or the month's number (1-12), respectively.

Listing 6: AdvancedMonths.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:ComboBox xmlns:mx="http://www.adobe.com/2006/mxml" 
    creationComplete="init()"
    dataProvider="{this.months}" 
    labelField="{this.labelFieldVar}">
  <mx:Script>
      <![CDATA[
         [Bindable]
         private var months:Array = [{full:"January",abr:"Jan",num:1},
                    {full:"February",abr:"Feb",num:2},
                    {full:"March",abr:"Mar",num:3},
                    {full:"April",abr:"Apr",num:4},
                    {full:"May",abr:"May",num:5},
                    {full:"June",abr:"Jun",num:6},
                    {full:"July",abr:"Jul",num:7},
                    {full:"August",abr:"Aug",num:8},
                    {full:"September",abr:"Sep",num:9},
                    {full:"October",abr:"Oct",num:10},
                    {full:"November",abr:"Nov",num:11},
                    {full:"December",abr:"Dec",num:12}]; 
         [Bindable]
         public var labelFieldVar:String = "full";
         
         public function getSelectedMonth(s:String):String{
             return "You have selected "+this.selectedItem[s];
         }
      ]]>
  </mx:Script>
</mx:ComboBox>

Notice that labelFieldVar is set as a public property. This is important because only public variables and methods can be accessed from the parent, and only public properties can be set from within the custom MXML tag that is used to add the component to the parent. The labelFieldVar is also set to be bindable, which allows the property to be bound to the component root tag's labelField property. Also notice that, when the variable is bound, it is accessed by scoping the variable name with this using dot notation. It is not necessary to scope it, because it is a local property, but it is a best practice to do so. If you do not use this, the variable is scoped to this by default.

To set the labelFieldVar, simply add the property to the new MXML tag. Following is an example showing all three versions of this component within a single application. Figure 4 shows all three versions of the AdvancedMonths component.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
    xmlns:comps="*" backgroundColor="#FFFFFF">
    <mx:Script>
        <![CDATA[
           import mx.controls.Alert; 
        ]]>
    </mx:Script>
  <mx:Panel title="Advanced Months" width="100%" height="100%" 
    paddingTop="10" paddingLeft="10" paddingRight="10" paddingBottom="10"
    layout="horizontal">
    <comps:AdvancedMonths id="full" labelFieldVar="full" 
        change="mx.controls.Alert.show(full.getSelectedMonth('full'))" />
    <comps:AdvancedMonths id="abr" labelFieldVar="abr" 
        change="mx.controls.Alert.show(abr.getSelectedMonth('abr'))"/>
    <comps:AdvancedMonths id="num" labelFieldVar="num" 
        change="mx.controls.Alert.show(num.getSelectedMonth('num'))" />
  </mx:Panel>
</mx:Application>

The AdvancedMonths component also has a public function added to demonstrate how methods can be added to component. In this example, the method will be called when the component's change event occurs, passing in the type of month that should be returned. An Alert will show the current value of the component. Although this example may not have any use in the real world, it is simply meant to show the process of calling a method within a component. In the next section, you see the value of calling methods on components within a more detailed example. Figure 5 shows the custom component's method being called from the parent.

Professional Adobe Flex 2 : Figure 4
Figure 4: All three versions of the AdvancedMonths component
Professional Adobe Flex 2 : Figure 5
Figure 5: The custom component's method being called from the parent

Excerpted from Chapter 12 "Creating MXML Components" of Professional Adobe Flex 2 by Rich Tretola, Simon Barber, Renaun (Wrox, 2007, ISBN: 978-0-470-10267-1). Rich Tretola is a Senior Software Developer at Herff Jones, Inc., specializing in Rich Internet Applications (RIAs). He is an award-winning Flex developer and self-proclaimed Flex evangelist. He entered the field of Web development in the late 1990s and has been building applications ever since. Other than Flex, he builds applications using ColdFusion, Flash, and Java (where he is a Sun-certified programmer). He is highly regarded within the Flex community as an expert in RIA and is also an Adobe Community Expert. He runs a popular Flex blog at http://www.everythingFlex.com, is a contributor to the Indianapolis Flex user group, and has appeared in the Web Developer's and Designer's Journal.