Modified

7 January 2008

Requirements

   
Prerequisites
To benefit most from this article, it is best if you are familiar with Flex Builder, object oriented techniques, and ActionScript 3.0.
 
User level
Intermediate
Required products
Flex Builder 3 (Download trial)
Flex 3 (Download trial)

Sample files

   

 
Additional Requirements

 
Eclipse 3.3
The file custom_comps.zip contains all of the source files for this article. To use the source files, create a new Flex project, and then extract the contents of custom_comps.zip in the root of the project.
 
Adobe Flex and Flex Builder are great tools to build a rich, highly functional web application. As a Java developer I was surprised at how familiar ActionScript 3.0 was, making the learning curve fairly flat.  Once I started using Flex, it was great to see that I could use most, if not all, of the approaches to writing software that I was used to.  This article presents two of my favorite approaches: creating components with a mixture of inheritance and composition, and enforcing separation of concerns, especially between view and business logic.
 

 
Starting out

When I started using the Visual Designer with Flex Builder, I was impressed with how quickly I could develop a GUI.  I was well on my way to completing a substantial part of the application when I began to notice something familiar.  As an experienced Java web developer, I learned over time that a JSP page should be used only for presentation.  All business logic should be in classes, and if you absolutely had to have code in your JSP page, you would wrap it up in a custom tag. 
 
I started looking at my Flex application and sure enough I had a bunch of MXML files with <mx:Script></mx:Script> tags that contained behaviors for my components. I felt the need to do what I learned from working on another platform—separate my view and business logic.  In this tutorial, I describe a practice called "code behind" where you define your visual component completely in ActionScript 3.0, but use MXML to decide how your component renders visually.
 
Note: This is an architectural approach for building an application. If you are doing quick prototyping, there is no need to always use the "code behind" technique.
 
At first it may seem a bit intimidating to do so, but once you get the hang of developing using this practice, it actually becomes much easier.
 
In brief, you extend an existing Flex visual component class and add properties that themselves are other visual components.  I will demonstrate this by building a very simple search interface using a DataGrid, TextInput, and two button objects.  Here is an overview of the process you use:
 
  1. Extend the DataGrid class to add behaviors needed to display search results.
  2. Extend the Panel class that will be the overall container for your component.
  3. Add private properties to your container class for the member objects.
  4. Add get and set methods for your variables.
  5. Add event listeners to your various components and add the behavior for each component depending on the action.
  6. Create an MXML file based on the class you created in Step 1 and add the components to that map to the private properties in Step 2 to the MXML file.
  7. Add your completed component view to your main application MXML file.
Now I will go over each step in detail.
 

 
Extending the DataGrid

You will extend the DataGrid with the class named SearchResultGrid.  The SearchResultGrid class will be fairly straightforward, displaying search results returned as XML.  
 
You create the SearchResultGrid class to:
 
  1. Add three arrays, one to map element values to columns, another to be the label for each column, and the third to hold DataGridColumn objects.
  2. Add the method displaySearchResults.
First, you declare three instance variables of type Array named fields, headers, and cols.  The fields array will contain strings to map the elements in the returned XML to display in the column of the same index.  The labels array will contain strings to be used as labels for the header of each column of the same index.  The cols array should be initialized to be an empty array of the same length as the fields or headers arrays.   You do the following by overriding the protected commitProperties method in SearchResultGrid class:  
 
  1. Loop over the fields (or labels) array.
  2. Create one DataGridColumn object.
  3. Set the dataField and headerText properties of the DataGridColumn object with the values from the fields and labels arrays respectively.
  4. Add the new DataGridColumn object to the cols array.
  5. Set the cols array to the columns property inherited from the DataGrid class.
The  commitProperties method should look something like the following:
 
override protected function commitProperties():void{ super.commitProperties(); _cols = new Array(_fields.length); for(var i:int=0;i<_fields.length;i++){ var column:DataGridColumn = new DataGridColumn(); column.dataField = _fields[i]; column.headerText = _labels[i]; _cols[i] = column; } this.columns = _cols; }
That is pretty much all there is to set up the SearchResultGrid class. The only thing left is to write the  displaySearchResults method.  The DisplaySearchResults method will take an XML object, drop it into a XMLListCollection object, and then set the dataProvider property equal to the collection. The results are automatically displayed in the SearchResultGrid. The method looks like the following:
 
public function displaySearchResults(search:XML):void{ this.dataProvider = new XMLListCollection(search.result); }
I don't think it could be any simpler to update your data display!  I am making two assumptions here that I should point out:
 
  1. The XML returned has one or more <result>...</result> elements.
  2. The number and name of elements within the <result>...</result> match the fields array defined earlier.

 
Extend the Panel class and add private properties

Now you can create the overall container for your search interface by extending the Panel class with the SearchPanel class.  The next step is to add a private property to the SearchPanel class of type SearchResultGrid.  After that you add properties for a TextInput, and two Button objects. At this point your SearchPanel class looks like the following:
 
public class SearchPanel extends Panel{ private var _searchResultGrid:SearchResultGrid; private var _searchButton:Button; private var _clearButton:Button; private var _searchTerm:TextInput;

 
Add get and set methods

At this point you need to define how to "wire up" the SearchPanel class with its objects.  This part is very simple.  For each private property defined above. you will have a get and set method. Each get and set method takes the following form:
 
public function set searchResultGrid(searchResultGrid:SearchResultGrid):void{ this._searchResultGrid = searchResultGrid; } public function get searchResultGrid():SearchResultGrid{ return this._searchResultGrid; }
Now you could just as easily declare each property as public, but let's make them private and use the accessor/mutator methods.  This way you have a "hook" when these objects are set or returned, in case there is any action you want your object to perform during the function call.  You will write a get and set method for each object that needs to be "injected" into our search panel by Flex.  Exactly how this is accomplished will be discussed a bit later when you get to the point where you will create the MXML file for the SearchPanel class. 
 

 
Add event listeners

At this point, you are almost done.  All that is left is to add event listeners to your button objects.  Now you might think that you should add the listeners to your button objects in the constructor for the SearchPanel class, I know I certainly did when I first used the code behind approach.  If you do that, you will effectively get an error complaining about not being able to access a method or property of a null object reference.  When the constructor for your SearchPanel class is called, the other objects contained within the SearchPanel class do not exist yet!  What you will do is to override the childrenCreated method and add event listeners there.  Any other properties that need to be set on your collaborator objects should also be done at this point.
 
At this point all of the mutator methods you defined earlier have been called, so it is safe to work with any of the dependent objects.    In the childrenCreated method, you will have the SearchButton and ClearButton listen for click events.  The SearchButton will call the doSearch method and use the text in the TextInput object.  The ClearButton will simply clear any text in the TextInput. You also set the fields and labels arrays for the SearchResultGrid described earlier.
 
 The childrenCreated  method looks something  like the following:
 
override protected function childrenCreated():void{ _searchButton.addEventListener(MouseEvent.CLICK,doSearch); _clearButton.addEventListener(MouseEvent.CLICK,clearText); _searchResultGrid.fields = new Array("title","author","date"); _searchResultGrid.labels = new Array("Title","Author","Publish Date"); }
All that is left now is to add the method doSearch.  The doSearch method will use the HttpService class to send the search string to your search endpoint.  The method onSearchComplete will be used for the callback once the response is returned from the server, and simply calls the displaySearchResults method of the searchResultsGrid object. Then your search results are displayed.
 
Now the search panel component is complete.
 

 
Create view for SearchPanel

The next step is to create a MXML file for the view representation of the component.  On the current project you have named the MXML files the same as the ActionScript class name with "View" on the end, so the MXML file for the SearchPanel would be SearchPanelView and look something like:
 
<?xml version="1.0" encoding="utf-8"?> <SearchPanel xmlns="bbejeck.example.*" xmlns:comp="bbejeck.example.*" xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"> <comp:SearchResultGrid id="searchResultGrid" percentWidth="100" percentHeight="60"/> <mx:TextInput id="searchTerm" x="215" y="320" /> <mx:Button x="215" y="350" id="searchButton" label="Search" /> <mx:Button x="315" y="350" id="clearButton" label="Clear"/> </SearchPanel>
As you can see, what you end up with is a very light, clean MXML file that only has view specific information in it. The key thing to note here is that the ID of each component maps back to a "set" method of the SearchPanel class.  This defines how the ActionScript class and MXML file end up being "wired " together.  You will also notice that you have a custom object  <comp:SearchResultGrid../> mixed with standard Flex objects such as the Button or TextInput class. 
 

 
Add component view to main MXML file

Now to use your newly crafted SearchPanelView in the application, you only need to add the following to your main MXML file:
 
<comp:SearchPanelView id="searchPanel" title="Search Panel" horizontalCenter="0" verticalCenter="0" width="600" height="500" />
As you can see, by using the "code behind" approach, your code is very concise and maintainable.  As you develop more complex applications, not only would you have a palate of components, but maintenance and changes to your applications would be very manageable.
 

 
Where to go from here

Now that you are done, here are some thoughts I would like to leave you with that I have found helpful:
 
  • To hook the SearchPanel to a live source, simply change the headers and labels arrays to match the XML being returned and set the URL property of the HTTPService object in the SearchPanel.
  • There's no need to create a MXML file for each individual object. Rather, create an MXML file for a component that is comprised of several other objects.
  • Name your MXML files using the "class name" and View pattern.
  • This is just an oversimplified example of how to build components in Flex. Explore some other approaches and play around to see what is possible.
  • Books, or articles on object oriented programming or algorithms are very helpful.
  • Two resources I have found in particularly helpful are