Learning Flex Basics (Part 4): Working with Data Models
In this tutorial, you work with data models both for importing data and storing data entered by users in Macromedia Flex applications. You also learn more about manipulating data in applications by using data entered in a form to create a new object, and by implementing drag and drop between different data grids.
This tutorial is the first part in a four-part series written by Robert Crooks of the Macromedia Customer Training group:
- Learning Flex Basics (Part 1): Creating Your First Flex Application
- Learning Flex Basics (Part 2): Creating a Flex Calculator
- Learning Flex Basics (Part 3): Working with Containers
If you like what you see and want to get more experience with Flex, Macromedia Customer Training offers Fast Track to Macromedia Flex, a two-day onsite training course to jump-start Flex development in your organization. Now let's get started!
As the final tutorial in this series, the aim is partly to familiarize you with additional elements of the Flex class library, and with some additional techniques such as implementing drag and drop interfaces. This tutorial also intends to make you more aware of how you can use Flex to separate out and loosen coupling between the view, data model, and controller portions of an application.
The controller methods know nothing about the details of the view, for instance.
The method that adds a new employee takes the data from a model, and so doesn't
need to know how you go about gathering the data to put there. Similarly,
the methods that handle the drag-and-drop operations work from methods and
properties of the event objects that Flex generates and the built-in DragManager
class, so they need not know where the data objects are dragged from and
dropped—in fact, all you need to do is add the dragEnabled="true"
attribute
to the target data grids to enable dragging items from one to the other;
the controller methods will work without modification.
What You Will Learn
- How to use the Application tag.
- How to use a Panel container.
- How to use the HBox.
- How to use the VBox.
- How to use the Form container.
- How to use TextInput controls.
- How to use Button controls.
- How to use DataGrid controls.
- How to use the Model class for importing data or gathering user input.
- How to enable drag and drop for items in a DataGrid.
- How to create event handlers for drag and drop operations in ActionScript.
Requirements
To complete this tutorial you will need to install the following software and files:
Macromedia Flex
(Installed and running on any server and platform supported by Flex. Note that the Flex trial is available on CD from the Macromedia store for $8.99. To learn more about the Flex trial, please read the Flex Frequently Asked Questions.)
Dreamweaver MX 2004 or a text editor that you can use to write XML and ActionScript code (a basic text editor such as Notepad is adequate)
Solution files for the code
- data.mxml, Employee.as, DataController.as, employees.xml, locations.xml from the zip for this article.
Prerequisite knowledge:
- Read A Brief Overview of Flex.
- You must know how to browse a Flex application.
- You should have completed
- Learning Flex Basics (Part 1): Creating Your First Flex Application
- Learning Flex Basics (Part 2): Creating a Flex Calculator
- Learning Flex Basics (Part 3): Working with Containers
Or you should have a basic understanding of how Flex applications work.
- If you have not completed the previous tutorials, create a directory call flex_tutorials under the Flex context root to save your code in.
Building the Application
The application view consists of three panels. One contains a DataGrid component that displays names and locations for all employees, and also provides a simple form for adding a new employee. The other two panels show DataGrids for the Engineering Group and Support Group. You implement drag and drop so that users can drag employees from the general employee grid and add them to either the engineering group or support group grid.
You use data models to import data for existing employees and office locations from XML files. A third model captures data from the employee entry form to add to the employee grid.
You create two associated ActionScript files. The DataController class contains methods to handle events in the application. The controller uses a separate Employee class as a factory to generate new employee data objects.

Figure 1. The finished application
Create the Application Object
To begin any Flex application, start with an XML declaration and an Application
tag.
The Application
tag will include a namespace declaration for the MX class
library: xmlns:mx="http://www.macromedia.com/2003/mxml"
. The mx prefix is
used with all tags for this library.
- Create a new file and save it as data.mxml in the flex_tutorials directory.
-
At the beginning of the file, insert the XML declaration:
<?xml version="1.0">
-
After the XML declaration, add an
Application
tag with the MXML namespace:<mx:Application xmlns:mx="http://www.macromedia.com/2003/mxml"> </mx:Application>
Create the Application Layout
You create the application layout using containers covered in Learning Flex Basics (Part 3): Working with Containers. Here, you use HBox, VBox, Panel, and Form containers to create a layout consisting of a main panel that contains a form, and two smaller panels placed to the right of the main panel.
-
Inside the Application block, add an HBox container with no properties:
<mx:HBox>
</mx:HBox> -
Inside the HBox container, add a Panel container with the following properties:
<mx:Panel id="empPanel" title="Employee Data" height="{engPanel.height+supPanel.height}"> </mx:Panel>
Note: Bind this panel height to the the sum of the heights of two panels you will create beside this one—this maintains the proportions of the application without hard coding the heights. You will bind the widths of the two panels on the right to this one in the same way later.
-
Inside the Panel, add a Form container with no properties:
<mx:Form> </mx:Form>
-
After the Form container, but still inside the Panel container, add a ControlBar container with no properties:
<mx:ControlBar> </mx:ControlBar>
-
After the Panel container, add a VBox container with no properties:
<mx:VBox> </mx:VBox>
-
Inside the VBox container, add another Panel with the properties as shown:
<mx:Panel id="engPanel" title="Engineering Group" width="{empPanel.width}"> </mx:Panel>
-
Still inside the VBox container, but after the Engineering Group Panel, add another Panel container with the properties as shown:
<mx:Panel id="supPanel" title="Support Group" width="{empPanel.width}"> </mx:Panel>
Add the Form Controls
The Form container and its child containers are covered in Learning Flex Basics (Part 3): Working with Containers. Here, you add controls for entry of new employee data: a couple of TextInput fields and a ComboBox whose dataProvider will be bound to locModel.location; you generate this array from an imported XML file in a later part of the tutorial.
After the form, add a button that appears to submit the form data, though you will see later that this not exactly what it does.
-
Inside the Form container block add a FormHeading with the label Add New:
<mx:FormHeading label="Add New"/>
-
After the FormHeading, add a FormItem with the label First Name:
<mx:FormItem label="First Name"> </mx:FormItem>
-
Inside the FormItem container block, add a TextInput with the
id
of fNameInput:<mx:TextInput id="fNameInput"/>
-
Add another FormItem with the label Last Name:
<mx:FormItem label="Last Name"> </mx:FormItem>
-
Inside the FormItem container block, add a TextInput with the
id
of lNameInput:<mx:TextInput id="lNameInput"/>
-
Add another FormItem with the label Location:
<mx:FormItem label="Location"> </mx:FormItem>
-
Inside the FormItem container block, add a ComboBox with the
id
of locCombo and the dataProvider bound tolocModel.location
:<mx:ComboBox id="locCombo" dataProvider="{locModel.location}"/>
-
Inside the ControlBar container that follows the Form block, add a Button with id and click properties as shown:
<mx:Button label="Add Employee" click="dataController.addNewEmp(newEmpModel,empGrid.dataProvider)"/>
Note: You will add the dataController and its method later - the arguments for the method will make sense then.
Add the DataGrid Controls
You now add instances of a class not used in the previous tutorials: the
DataGrid control. The DataGrid control is a powerful data visualization
tool; its columns are sortable and cells are editable. The DataGrid
control takes an array of objects as its dataProvider. The columns are
specified inside a nested <columns>
block
as an array of DataGridColumn containers. The columnName
property
value will be one of the properties of the objects in the dataProvider. The
general syntax for the DataGrid control is as follows:
<mx:DataGrid> <mx:dataProvider> {some array of objects} </mx:dataProvider> <mx:columns> <mx:Array> <mx:DataGridColumn columnName="propertyName1" headerText="some text" /> <mx:DataGridColumn columnName="propertyName2" headerText="some text" /> </mx:Array> </mx:columns> </mx:DataGrid>
To enable drag and drop of DataGrid items, set dragEnabled="true"
.
To enable users to drag data objects and drop them into a grid, there
are several drag-related events you can set handlers for, but the essential
ones are:
- dragEnter: broadcast when an item is dragged over the grid
- dragDrop: broadcast when an item is dropped onto the grid
In the following steps, you will enable users to drag items from an employee grid and drop them onto either an engineering or support grid. You will write the handlers for the events as methods of the controller later.
-
Inside the first Panel container, above the Form container block, add a DataGrid container with the
id="empGrid"
anddragEnabled="true"
:<mx:DataGrid id="empGrid" dragEnabled="true"> </mx:DataGrid>
-
Inside the DataGrid container, add a dataProvider block with a binding to empModel.employee:
<mx:dataProvider> {empModel.employee} </mx:dataProvider>
Note: You will generate the data for the dataProvider later.
-
After the dataProvider block, but still inside the DataGrid block, add a columns block with a nested Array block:
<mx:columns> <mx:Array> </mx:Array> </mx:columns>
-
Inside the Array block, add a DataGridColumn with name fName and headerText First Name:
<mx:DataGridColumn columnName="fName" headerText="First Name" />
-
Add two more DataGridColumn containers with the following properties:
- columnName="lName" (that's a lowercase L)
- headerText="Last Name"
- columnName="location"
- headerText="Location"
<mx:DataGridColumn columnName="lName" headerText="Last Name" /> <mx:DataGridColumn columnName="location" headerText="Location" />
- Copy the entire DataGrid code block to the clipboard and paste it inside the Panel with
id="engPanel"
. -
In the new DataGrid container, remove the dataProvider block, the
id
property, and thedragEnabled
property, and add thedragEnter
anddragDrop
properties with values as follows:- dragEnter="dataController.doDragEnter(event)"
- dragDrop="dataController.doDragDrop(event)"
<mx:DataGrid dragEnter="dataController.doDragEnter(event)" dragDrop="dataController.doDragDrop(event)"> <mx:columns> <mx:Array> <mx:DataGridColumn columnName="fName" headerText="First Name" /> <mx:DataGridColumn columnName="lName" headerText="Last Name" /> <mx:DataGridColumn columnName="location" headerText="Location" /> </mx:Array> </mx:columns> </mx:DataGrid>
-
Copy the entire DataGrid code block from the Engineering Group Panel to the clipboard and paste it inside the Panel with
id="supPanel"
. No changes to the code are required.Note: You should now have three DataGrid blocks, one in each of the Panels. The second and third have identical code. Look at the full code listing for data.mxml if you are not sure you have the correct code.
Add the New Employee Model
The Model component in Flex is simple to implement but deceptively powerful. Here you use it to capture user input data, so that the handler method for the data can be decoupled from the actual form elements. The key to using the Model in this way to recognize that it generates an ActionScript object. By nesting XML tags inside the Model block, you are adding properties to the object. Therefore, the MXML and ActionScript code blocks below create essentially the same object:
MXML: <mx:Model id="myModel"> <name>Maya</name> <position>System Architect</position> </mx:Model> ActionScript: var myModel:Object=new Object(); myModel.name="Maya"; myModel.position="System Architect";
One of the advantages of using the MXML model here is that you can bind the
properties of the object to data values using curly braces: <name>{nameInput.text}</name>.
-
At the beginning of the
Application
block, add a Model withid="newEmpModel"
:<mx:Model id="newEmpModel"> </mx:Model>
-
Inside the Model block, add the properties
fName
,lName
, andlocation
as nested XML tags:<fName></fName> <lName></lName> <location></location>
-
Bind the three XML tags to
fNameInput.text
,lNameInput.text
, andlocCombo.selectedItem
:<fName>{fNameInput.text}</fName> <lName>{lNameInput.text}</lName> <location>{locCombo.selectedItem}</location>
Note: These bindings are to properties of the Form controls you created earlier.
Add the Employee and Location Models
You now add two additional Model components that serve to import external data from XML files. Another benefit of the Model component is that it can take well-formed XML and automatically transform it transparently into an ActionScript data structure. To import an XML file, simply set the source attribute of the Model to the full or partial URL for the XML file.
- Open employees.xml or create it from the code listing. Note that inside the root node allEmployees contains two employee nodes with identical structures. These will become an array called employee that will be a property of the Model.
-
After the newEmpModel Model block, add a Model with
id="empModel"
andsource="employees.xml"
:<mx:Model id="empModel" source="employees.xml"/>
- Open locations.xml or create it from the code listing.
Note that inside the root node locations contains a series of location
nodes. These will become an array called
location
that will be a property of the Model. -
After the empModel, add another Model with
id="locModel"
andsource="locations.xml"
:<mx:Model id="locModel" source="locations.xml"/>
Note: You created binding to these models when you created the ComboBox and empGrid DataGrid earlier. You may want to review this code to identify the bindings.
Instantiate the DataController Class
To complete the data.mxml file, you instantiate the DataController class (which you build later). This is a straightforward procedure that is discussed in more detail in Learning Flex Basics (Part 2): Creating a Flex Calculator. You can instantiate an ActionScript class using a tag in an MXML file, but you must include a namespace reference for the location of the class so that the Flex compiler can locate it.
-
Add an additional
xmlns
property to theApplication
tag; give the value of the namespace the value of "*" and do not assign a prefix to it:<mx:Application xmlns:mx="http://www.macromedia.com/2003/mxml" xmlns="*">
-
After the Model tags, add a DataController tag with an
id="dataController"
:<DataController id="dataController"/>
Note: You have already referenced this object in specifying handlers for the Button and DataGrids.
- Save the file. The data.mxml file is now complete.
Create the Employee Class
You now create the first of two external ActionScript classes required for the application. The Employee.as class is not used directly by data.mxml; instead it is imported into the DataController class you build next and used there as factory to generate new employee objects.
The class is simple, consisting of an empty constructor, properties for the employee name and location, and a method that assigns passed values to these properties.
The general structure of ActionScript classes is covered in Learning Flex Basics (Part 2): Creating a Flex Calculator.
- Open a new file in your editor and save it as Employee.as in the flex_tutorials directory.
-
Add a class definition and empty constructor for the Employee class:
class Employee { // constructor public function Employee() {} }
-
Above the constructor, add three undefined properties of type String for
fName
,lName
, andlocation
:public var fName:String; public var lName:String; public var location:String;
-
After the constructor, add a public method called
addEmpData
that takes the parametersfName
,lName
, andlocation
(all type String), and sets the properties of the same name equal to the passed parameters:public function addEmpData(fName:String,lName:String,location:String):Void { this.fName=fName; this.lName=lName; this.location=location; }
- Save the file.
Create the DataController Class
Next you create the DataController class, for which you created the instantiation in data.mxml earlier. This class contains methods used to handle events in data.mxml. This class will import two addtional classes:
- Employee: The ActionScript class you just created
- mx.managers.DragManager: A static class from the Flex class library that contains properties and methods used in handling drag-and-drop event
You will use only a single property of the DragManager class here, but it has much more functionality that can help you implement business rules around drag and drop operations. Consult the Flex documentation for more details.
- Open a new file in your editor and save it as DataController.as in the flex_tutorials directory.
-
At the top of the file, add import statements to import the Employee and mx.managers.DragManager classes:
import mx.managers.DragManager; import Employee;
-
After the import statements, add a class definition with an empty constructor for the DataController class:
class DataController { // constructor public function DataController() {} }
Add the DataController Methods
The DataController has three methods:
-
The
addNewEmp()
method takes asrc
object that hasfName
,lName
, andlocation
properties and generates a temporary instance of the Employee class from it. The temporary object is added to adest
dataProvider. Because thesrc
anddest
are passed as parameters, the method can handle copying data for both the button click and drag and drop operations.Note: Creating the temporary object rather than adding the src object directly is necessary because objects are passed by reference, and therefore any later changes to the src object would be automatically reflected in the dest dataProvider.
- The
doDragEnter()
method handles the event of a dragged object passing over an object. On this event you define whether the object can accept the the dragged object using theevent.handled
property (the default is "false") and if so, what DragManager operation should be carried out if the object is dropped (the default is NONE) - The
doDragDrop()
method handles the event of a dragged object being dropped. The logic is facilitated by properties and methods of theevent
object that Flex automatically generates and passes to handlers:event.dragSource.dataForFormat("items")
automatically returns an array containing the dragged data objectsevent.target
returns the object capable of recieving the data in the location where the drop event occurs (in the case of a DataGrid, this property will be the grid's dataProvider)
-
After the DataController constructor, add a public method called addNewEmp() that takes two parameters called
src
anddest
(both type Object) and returns nothing:public function addNewEmp(src:Object,dest:Object):Void { }
-
Inside the method block, declare a variable called
tmpObj
of type Employee, and initialize it as a new Employee object:var tmpObj:Employee=new Employee();
-
Invoke the Employee
addEmpData()
method ontmpObj
, passing thefName
,lName
, andlocation
properties ofsrc
as arguments:tmpObj.addEmpData(src.fName,src.lName,src.location);
-
Invoke the dataProvider
addItem()
method ondest
to addtmpObj
:dest.addItem(tmpObj);
-
Add another method called
doDragEnter()
that takes a parameter called event; inside the method block setevent.handled=true
andevent.action=DragManager.COPY
:public function doDragEnter(event):Void { event.handled=true; event.action=DragManager.COPY; }
-
Add one more method called
doDragDrop()
that also takes anevent
parameter; inside the method block set a variable calleditems
equal toevent.dragSource.dataForFormat("items")
and another variable calleddest
equal toevent.target
:public function doDragDrop(event):Void { var items = event.dragSource.dataForFormat("items"); var dest = event.target; }
-
Add one more line to method that calls the
addNewEmp()
method to copy the dragged data to the target DataGrid, passing the first element of theitems
array anddest
as arguments :addNewEmp(items[0],dest);
- You're done! Save the file, and then browse data.mxml application to test it. Try adding an new employee, and also dragging employees into the Engineering or Support Group DataGrids.
Complete Code Listing for data.mxml
<?xml version="1.0"?> <mx:Application xmlns:mx="http://www.macromedia.com/2003/mxml" xmlns="*"> <mx:Model id="newEmpModel"> <fName>{fNameInput.text}</fName> <lName>{lNameInput.text}</lName> <location>{locCombo.selectedItem}</location> </mx:Model> <mx:Model id="empModel" source="employees.xml"/> <mx:Model id="locModel" source="locations.xml"/> <DataController id="dataController"/> <mx:HBox> <mx:Panel id="empPanel" title="Employee Data" height="{engPanel.height+supPanel.height}"> <mx:DataGrid id="empGrid" dragEnabled="true"> <mx:dataProvider> {empModel.employee} </mx:dataProvider> <mx:columns> <mx:Array> <mx:DataGridColumn columnName="fName" headerText="First Name" /> <mx:DataGridColumn columnName="lName" headerText="Last Name" /> <mx:DataGridColumn columnName="location" headerText="Location" /> </mx:Array> </mx:columns> </mx:DataGrid> <mx:Form> <mx:FormHeading label="Add New"/> <mx:FormItem label="First Name"> <mx:TextInput id="fNameInput"/> </mx:FormItem> <mx:FormItem label="Last Name"> <mx:TextInput id="lNameInput"/> </mx:FormItem> <mx:FormItem label="Location"> <mx:ComboBox id="locCombo" dataProvider="{locModel.location}"/> </mx:FormItem> </mx:Form> <mx:ControlBar> <mx:Button label="Add Employee" click="dataController.addNewEmp(newEmpModel,empGrid.dataProvider)"/> </mx:ControlBar> </mx:Panel> <mx:VBox> <mx:Panel id="engPanel" title="Engineering Group" width="{empPanel.width}"> <mx:DataGrid dragEnter="dataController.doDragEnter(event)" dragDrop="dataController.doDragDrop(event)"> <mx:columns> <mx:Array> <mx:DataGridColumn columnName="fName" headerText="First Name" /> <mx:DataGridColumn columnName="lName" headerText="Last Name" /> <mx:DataGridColumn columnName="location" headerText="Location" /> </mx:Array> </mx:columns> </mx:DataGrid> </mx:Panel> <mx:Panel id="supPanel" title="Support Group" width="{empPanel.width}"> <mx:DataGrid dragEnter="dataController.doDragEnter(event)" dragDrop="dataController.doDragDrop(event)"> <mx:columns> <mx:Array> <mx:DataGridColumn columnName="fName" headerText="First Name" /> <mx:DataGridColumn columnName="lName" headerText="Last Name" /> <mx:DataGridColumn columnName="location" headerText="Location" /> </mx:Array> </mx:columns> </mx:DataGrid> </mx:Panel> </mx:VBox> </mx:HBox> </mx:Application>
Complete Code Listing for Employee.as
class Employee { // properties public var fName:String; public var lName:String; public var location:String; // constructor public function Employee() {} // methods public function addEmpData(fName:String,lName:String,location:String):Void { this.fName=fName; this.lName=lName; this.location=location; } }
Complete Code Listing for DataController.as
import mx.managers.DragManager; import Employee; class DataController { // constructor public function DataController() {} // methods public function addNewEmp(src:Object,dest:Object):Void { // create an Employee object var tmpObj:Employee=new Employee(); // add the new data to the object properties tmpObj.addEmpData(src.fName,src.lName,src.location); // add the employee object to the destination dataProvider dest.addItem(tmpObj); } public function doDragEnter(event):Void { // allow drop event.handled=true; // set action to copy event.action = DragManager.COPY; } public function doDragDrop(event):Void { /* event.dragSource.dataForFormat("items") returns an array of dragged data items */ var items = event.dragSource.dataForFormat("items"); // for convenience, set var for drop target var dest = event.target; // copy the item target's dataProvider addNewEmp(items[0],dest); } }
Complete Code Listing for employees.xml
Note: This is a prebuilt data file; if you need to build your own copy, copy the data below into an empty text file and save it as employees.xml in the flex_tutorials directory.
<allEmployees> <employee> <fName> Clare </fName> <lName> DuPre </lName> <location> Paris </location> </employee> <employee> <fName> David </fName> <lName> Green </lName> <location> San Francisco </location> </employee> </allEmployees>
Complete Code Listing for locations.xml
Note: this is a pre-built data file; if you need to build your own copy, copy the data below into an empty text file and save it as locations.xml in the flex_tutorials directory.
<locations> <location> Paris </location> <location> Tokyo </location> <location> Cairo </location> <location> San Franscisco </location> </locations>
Where to Go from Here
Continue on to the following tutorials to learn more:
- Learning Flex Basics (Part 1): Creating Your First Flex Application
- Learning Flex Basics (Part 2): Creating a Flex Calculator
- Learning Flex Basics (Part 3): Working with Containers
If you like what you see and want to get more experience with Flex, Macromedia Customer Training offers Fast Track to Macromedia Flex, a two-day onsite training course to jump-start Flex development in your organization. You should also look at the informative set of sample applications in included with Flex; these illustrate the use of many Flex features. You can also read the next tutorial online at: