Learning Flex Basics (Part 4): Working with Data Models
Robert Crooks
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.
If you like what you see and want to get more experience with Flex,
Macromedia Customer Training offers Developing Rich Internet Applications with 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
A Text Editor
Flex Builder, 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). You can download Flex Builder with the Flex server try/buy links above. Use the following links to try/buy Dreamweaver MX 2004:
Solution files for the code
Prerequisite knowledge:
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:
-
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:
-
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:
-
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:
-
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 to locModel.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"
and dragEnabled="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 the dragEnabled
property, and add the dragEnter
and dragDrop
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 with id="newEmpModel"
:
<mx:Model id="newEmpModel">
</mx:Model>
-
Inside the Model block, add the properties fName
, lName
, and location
as nested XML tags:
<fName></fName>
<lName></lName>
<location></location>
-
Bind the three XML tags to fNameInput.text
, lNameInput.text
, and locCombo.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"
and source="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"
and source="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 the Application
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.
- 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
, and location
:
public var fName:String;
public var lName:String;
public var location:String;
-
After the constructor, add a public method called addEmpData
that takes the parameters fName
, lName
, and location
(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 a src
object that has fName
, lName
, and location
properties and generates a temporary instance of the Employee class from it. The temporary object is added to a dest
dataProvider. Because the src
and dest
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 the event.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 the event
object that Flex automatically generates and passes to handlers:
event.dragSource.dataForFormat("items")
automatically returns an array containing the dragged data objects
event.target
returns the object capable of receiving 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
and dest
(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 on tmpObj
, passing the fName
, lName
, and location
properties of src
as arguments:
tmpObj.addEmpData(src.fName,src.lName,src.location);
-
Invoke the dataProvider addItem()
method on dest
to add tmpObj
:
-
Add another method called doDragEnter()
that takes a parameter called event; inside the method block set event.handled=true
and event.action=DragManager.COPY
:
public function doDragEnter(event):Void
{
event.handled=true;
event.action=DragManager.COPY;
}
-
Add one more method called doDragDrop()
that also takes an event
parameter; inside the method block set a variable called items
equal to event.dragSource.dataForFormat("items")
and another variable called dest
equal to event.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 the items
array and dest
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:
If you prefer, you can download the entire set of tutorials,
Learning Flex Series (Parts 1-4). 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:
About the author
Robert Crooks is the Director of Curriculum Development for Macromedia. He has written several courses on creating internet applications including the beta version of Fast Track to Macromedia Flex.