by Adobe
Adobe logo

Created

19 June 2011

Connect to data

 

Code

FlexMobileTestDriveHomeView.mxml
 
<?xml version="1.0" encoding="utf-8"?> <s:View xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:employeeservice="services.employeeservice.*" title="HomeView"> <fx:Script> <![CDATA[ import mx.events.FlexEvent; import mx.rpc.events.ResultEvent; protected function empList_creationCompleteHandler(event:FlexEvent):void { getEmployeesSummaryResult.token = employeeService.getEmployeesSummary(); } protected function doSearch(event:Event):void { this.addElement(busyIndicator); if(searchTxt.text!="") { getEmployeesSummaryResult.token = employeeService.getEmployeesSummaryByName(searchTxt.text); } else { getEmployeesSummaryResult.token = employeeService.getEmployeesSummary(); } } protected function getEmployeesSummaryResult_resultHandler(event:ResultEvent):void { this.removeElement(busyIndicator); } ]]> </fx:Script> <fx:Declarations> <s:CallResponder id="getEmployeesSummaryResult" result="getEmployeesSummaryResult_resultHandler(event)"/> <employeeservice:EmployeeService id="employeeService"/> </fx:Declarations> <s:titleContent> <s:TextInput id="searchTxt" width="100%" prompt="Employee Name" enter="doSearch(event)"/> </s:titleContent> <s:actionContent> <s:Button id="searchBtn" click="doSearch(event)" …/> <s:Button id="addBtn"…./> </s:actionContent> <s:List id="empList" left="0" right="0" top="0" bottom="0" creationComplete="empList_creationCompleteHandler(event)"> <s:AsyncListView list="{getEmployeesSummaryResult.lastResult}"/> <s:itemRenderer> <fx:Component> <s:IconItemRenderer iconHeight="50" iconWidth="50" iconFunction="getPhotoURL" labelFunction="getEmployeeFullName" messageField="title"> <fx:Script> <![CDATA[ private function getPhotoURL(item:Object):String { return "http://localhost/TestDrive/photos/" + item.photofile; } private function getEmployeeFullName(item:Object):String { return item.firstname + " " + item.lastname; } ]]> </fx:Script> </s:IconItemRenderer> </fx:Component> </s:itemRenderer> </s:List> <s:BusyIndicator id="busyIndicator" verticalCenter="0" horizontalCenter="0" symbolColor="red"/> </s:View>

Tutorial

In this tutorial, you retrieve data from a database and display it in the FlexMobileTestDriveHomeView of the application. You create a Flex data service that uses Flash Remoting to call methods of a PHP class, a ColdFusion component, or a Java class and then bind its operations to components in the application. You display the employee data in the List control and then customize the display using functions and renderers. Lastly, you add search functionality to the application.
 

 
Step 1: Create a Flex data service.

Use the Data menu and the Service Wizard to create a service for your application server.  For PHP using Flash Builder 4.5 for PHP, select PHP by Zend (see Figure 1); for PHP using Flash Builder 4.5, select PHP; for Java, select BlazeDS and for ColdFusion, select ColdFusion. For ColdFusion and PHP, browse to the service file you put on your application server earlier (see Figure 2 for a PHP example). For Java, import the employeeService destination, and change the service package to services.employeeservice (see Figure 3).
 
Create a data service for your application server (PHP example).
Figure 1. Create a data service for your application server (PHP example).
Configure the data service (PHP example).
Figure 2. Configure the data service (PHP example).
PHP developers: Flash Builder uses the Zend Framework to introspect the service and make Flash Remoting calls. If this is your first time creating a PHP service, Flash Builder will install the Zend Framework.
 
ColdFusion developers: RDS must be enabled on your server for Flash Builder to create a data service. If you have RDS configured to use a password, you will get an Authentication Required dialog box where you must enter the password or the username and password.
 
For Java, select the service(s) to import.
Figure 3. For Java, select the service(s) to import.
Java developers: The testdrive application was configured to use RDS with no password (in the web.xml file); if you get an Authentication Required dialog box, select No password required. You are also changing the service package (where the generated service classes will be located) so it matches the location referenced in the solution code, which can be used with PHP, ColdFusion, or Java servers.
 
Flash Builder introspects the server-side class file and creates a corresponding client-side class with the same operations. You can see your new data service in the Data/Services view (see Figure 4).
 
Locate your new service in the Data/Services panel.
Figure 4. Locate your new service in the Data/Services panel.
Note: Depending upon the application server you are using, some of the symbols and data types you see may differ slightly from those shown in Figure 4.
 

 
Step 2: Connect a service operation to the List component.

In Design mode, drag the getEmployeesSummary() operation from the Data/Services view onto the List component or select Bind to Data from the List component context menu. For PHP and ColdFusion, configure the return type (see Figure 5) by auto-detecting it from sample data and have it create an array of Employee objects (see Figure 6). For Java, configure the return type (see Figure 5) and use the existing Employee[] data type (see Figure 7). For the label field, select lastname (see Figure 8).
 
PHP and ColdFusion developers: Before Flash Builder can bind the results of the operation to a component, it needs to know what to do with the data returned from the operation. You are telling it to create an array of Employee objects so Flash Builder creates an Employee ActionScript class file with matching properties and uses that. You could also write your PHP classes and ColdFusion component methods to return strongly typed objects instead of general objects.
 
Bind the List to the getEmployeesSummary() operation.
Figure 5. Bind the List to the getEmployeesSummary() operation.
For PHP and ColdFusion, specify the return type for the getEmployeesSummary() operation.
Figure 6. For PHP and ColdFusion, specify the return type for the getEmployeesSummary() operation.
For Java, specify the return type to be the existing data type, Employee[].
Figure 7. For Java, specify the return type to be the existing data type, Employee[].
The List component is bound to the result of the service call, an array of Employee objects. You need to specify which property of the Employee objects you want to display in the List, in this case, you select lastname.
 
Select lastname to display in the List.
Figure 8. Select lastname to display in the List.

 
Step 3: Look at the generated code.

Locate the new files in the Package Explorer and look at the generated code in FlexMobileTestDriveHomeView.mxml.
 
The List component has a new creationComplete attribute. When the creationComplete event occurs, the empList_creationCompleteHandler() function is called and an event object is passed to it. The List creationComplete event is broadcast after the List has been created and all of its properties are set, including its size and position. The event object passed to the function is an instance of the Event class (in this case a FlexEvent) and has properties containing information about the event that occurred. The data to be displayed in the List is held in the list property of the AsyncListView instance and is set equal to the data returned from the service call. The labelField property specifies what property of the List data should be displayed for each list item.
 
<s:List id="empList" left="0" right="0" top="0" bottom="0" creationComplete="empList_creationCompleteHandler(event)" labelField="lastname"> <s:AsyncListView list="{getEmployeesSummaryResult.lastResult}"/> </s:List>
An instance of your EmployeeService data service is created inside the Declarations block. You place tags for all nonvisual objects inside the Declaration tag set. The green color of the tag indicates it is a compiler tag associated with compiler instructions and not an instance of a class.
 
<fx:Declarations> <s:CallResponder id="getEmployeesSummaryResult"/> <employeeservice:EmployeeService id="employeeService"/> </fx:Declarations>
The empList_creationCompleteHandler() function inside the Script block calls the getEmployeesSummary() method of the data service. You place all ActionScript code (which can only include property and method declarations) inside the Script compiler tag.
 
protected function empList_creationCompleteHandler(event:FlexEvent):void { getEmployeesSummaryResult.token = employeeService.getEmployeesSummary(); }
When this code is executed, the application makes a call to the server. This happens asynchronously in the background; the user can still interact with the application.
 
To handle successful results, a CallResponder object was created:
 
<fx:Declarations> <s:CallResponder id="getEmployeesSummaryResult"/> <employeeservice:EmployeeService id="employeeService"/> </fx:Declarations>
Now it needs to be associated with the service call.
 
When a service call is initiated, an instance of the AsyncToken class is created. To associate the CallResponder object with the service call, you set the CallResponder's token property equal to the AsyncToken generated at the time the service call is made. Now when data is returned from the server, it is handled by the CallResponder object, which, in addition to getting its lastResult property set; it also has result and fault events for which you can set handlers.
 
getEmployeesSummaryResult.token = employeeService.getEmployeesSummary();
Finally, the dataProvider property of the List component is set equal to an instance of the AyncListView class whose list property is bound to the lastResult property of the CallResponder object. This means that whenever the value of getEmployeesSummeryResult.lastResult changes at runtime, the data associated with the List is updated and the List will repopulate itself with the new data.
 
<s:List id="empList" left="0" right="0" top="0" bottom="0"Smar creationComplete="empList_creationCompleteHandler(event)" labelField="lastname"> <s:AsyncListView list="{getEmployeesSummaryResult.lastResult}"/> </s:List>
dataProvider is the default property of the List component so it does not have to be included as a child tag as shown below; when left out, it is automatically set equal to the AsyncListView instance.
 
<s:List id="empList" left="0" right="0" top="0" bottom="0"Smar creationComplete="empList_creationCompleteHandler(event)" labelField="lastname"> <s:dataProvider> <s:AsyncListView list="{getEmployeesSummaryResult.lastResult}"/> </s:dataProvider> </s:List>

 
Step 4: See the dynamic data displayed in the application.

Run the application and see the data retrieved from the database displayed (see Figure 9). Run either or both of your run configurations for different pixel density devices.
 
See the dynamic employee data displayed in the application
Figure 9. See the dynamic employee data displayed in the application.
You can scroll through the list by dragging it with the mouse, similar to how you would scroll with touch on a mobile device.
 

 
Step 5: Customize the List to display the employee's full name.

Set the List component's labelFunction property equal to function called getEmployeeFullName. Create a private function called getEmployeeFullName that receives one argument, an Object, and returns a String. Inside the function, concatenate and return the firstname and lastname properties of the Object passed to the function.
 
In Step 2, you specified that the List control display the lastname property of its dataProvider; the List control's labelField property was set to lastname. If you want to customize the text to be displayed in the control instead of just displaying the value contained in a specific field of the dataProvider, you can instead set the List control's labelFunction property equal to a function to be called before an item is displayed in the control.
 
<s:List id="empList" left="0" right="0" top="0" bottom="0" creationComplete="empList_creationCompleteHandler(event)" labelField="lastname" labelFunction="getEmployeeFullName"> <s:AsyncListView list="{getEmployeesSummaryResult.lastResult}"/> </s:List>
Define the getEmployeeFullName() method inside the Script block.
 
private function getEmployeeFullName(item:Object):String { return item.firstname + " " + item.lastname; }
The labelField property of the List component is no longer needed, so you can delete it.
 
Run the application and see the full names of the employees displayed in the application (see Figure 10).
 
See the full employee names displayed in the application.
Figure 10. See the full employee names displayed in the application.

 
Step 6: Customize the List to display an image next to the employee's name.

Set the List itemRenderer property equal to an instance of the IconItemRenderer class. Use the Component tag to define this class inline right in the MXML tag. Set the iconHeight and iconWidth properties to 50 and its iconFunction property to a function called getPhotoURL. Create a Script block for the renderer class and inside it, define a private function called getPhotoURL that receives one argument, an Object, and returns a String. Inside the function, return a string referencing the photofile property of the Object passed to the function and using the appropriate path to the photos folder on your application server.
 
Many Flex components use item renderers to define how to display associated data in the component. You can define your own ItemRenderer class or use one of several included in the Flex framework. The Flex 4.5 framework includes IconItemRenderer, which been optimized for use on mobile devices. It displays four optional parts for each item in a list-based control: an icon on the left defined by the iconField or iconFunction property, a single-line text label next to the icon defined by the labelField or labelFunction property, a multiline message below the text label defined by the messageField or messageFunction property, and a decorator icon on the right defined by the decorator property (see Figure 11).
 
The parts of the IconItemRenderer class.
Figure 11. The parts of the IconItemRenderer class.
The List component has an itemRenderer property that you set equal to the ItemRenderer class to be used to render the data items. You can create the renderer as a separate ActionScript class that extends the IconItemRenderer class and set the List itemRenderer property equal to the fully qualified name of that class. For convenience, so you don't have to create and maintain a separate file, you can also use the Component compiler tag to define a new class inline. In the code below, a new class is defined that extends the IconItemRenderer class.
 
<s:List id="empList" left="0" right="0" top="0" bottom="0" creationComplete="empList_creationCompleteHandler(event)"> <s:AsyncListView list="{getEmployeesSummaryResult.lastResult}"/> <s:itemRenderer> <fx:Component> <s:IconItemRenderer> </s:IconItemRenderer> </fx:Component> </s:itemRenderer> </s:List>
To display an image next to the List label, you can set the IconItemRenderer iconField, iconFunction, iconHeight, and iconWidth properties. If the dataProvider for the List control had a property containing the full URL for the image to be displayed, you could just set the iconField property to the name of that property. But it doesn't; it has a property called photofile equal to the name of the associated employee image, for example, jdoe.jpg. Thus, you need to set the iconFunction property instead of the iconField property, very similar to how you specifed a labelFunction instead of a labelField in Step 5.
 
IMPORTANT: The getPhotoURL() method must be defined inside the Script block for the IconItemRenderer class. Make sure you set the image path appropriately for your local application server.
 
<s:List id="empList" left="0" right="0" top="0" bottom="0" creationComplete="empList_creationCompleteHandler(event)" labelFunction="getEmployeeFullName"> <s:AsyncListView list="{getEmployeesSummaryResult.lastResult}"/> <s:itemRenderer> <fx:Component> <s:IconItemRenderer iconHeight="50" iconWidth="50" iconFunction="getPhotoURL"> <fx:Script> <![CDATA[ private function getPhotoURL(item:Object):String { return "http://localhost/TestDrive/photos/" + item.photofile; } ]]> </fx:Script> </s:IconItemRenderer> </fx:Component> </s:itemRenderer> </s:List>
Run the application and see employee images displayed in the application (see Figure 12).
 
See images next to the employee names in the application.
Figure 12. See images next to the employee names in the application.
 
Step 7: Move the labelFunction code into the IconItemRenderer.
Move the labelFunction property from the List tag to the IconItemRenderer tag. Move the getEmployeeFullName() method from the FlexMobileTestDriveHomeView Script block to IconItemRenderer Script block.
 
All of the code customizing the renderer is now contained in the renderer class definition.
 
<s:List id="empList" left="0" right="0" top="0" bottom="0" creationComplete="empList_creationCompleteHandler(event)"> <s:AsyncListView list="{getEmployeesSummaryResult.lastResult}"/> <s:itemRenderer> <fx:Component> <s:IconItemRenderer iconHeight="50" iconWidth="50" iconFunction="getPhotoURL" labelFunction="getEmployeeFullName"> <fx:Script> <![CDATA[ private function getPhotoURL(item:Object):String { return "http://localhost/TestDrive/photos/" + item.photofile; } private function getEmployeeFullName(item:Object):String { return item.firstname + " " + item.lastname; } ]]> </fx:Script> </s:IconItemRenderer> </fx:Component> </s:itemRenderer> </s:List>

 
Step 8: Customize the List to display a message under the employee's name.

Set the IconItemRenderer messageField property to title.
 
The IconItemRenderer class has a property called messageField that you can set equal to a property of the dataProvider that you want to display under the label.
 
<s:IconItemRenderer iconHeight="50" iconWidth="50" iconFunction="getPhotoURL" labelFunction="getEmployeeFullName" messageField="title">
Run the application and see employee titles displayed under the employee names in the application (see Figure 13).
 
See employee titles displayed under the employee names in the application.
Figure 13. See employee titles displayed under the employee names in the application.

 
Step 9: Load new data when the Search button is clicked.

Using the Data/Services view, right-click the getEmployeesSummaryByName() operation and configure the input type to be a String and the return type to be an existing data type, an array of Employee objects (see Figure 14). In Design mode, drag the getEmployeesSummaryByName() operation onto the Search button. Inside the generated event handler, pass the user-entered value which is held in searchTxt.text to the operation. Change the responder to be the existing getEmployeesSummaryResult responder and delete the newly generated getEmployeesSummaryByNameResult responder.
 
Specify the return type for the getEmployeesSummaryByName() operation.
Figure 14. Specify the return type for the getEmployeesSummaryByName() operation.
By default, a new responder getEmployeesSummaryByNameResult is created:
 
<s:CallResponder id="getEmployeesSummaryByNameResult"/>
... and used for the service call:
 
protected function searchBtn_clickHandler(event:MouseEvent):void { getEmployeesSummaryByNameResult.token = employeeService.getEmployeesSummaryByName(searchTxt.text); }
You want the results to be displayed in the existing List, though, so change the responder to be the existing getEmployeesSummaryResult responder whose lastResult property is already bound to the dataProvider of the List:
 
protected function searchBtn_clickHandler(event:MouseEvent):void { getEmployeesSummaryResult.token = employeeService.getEmployeesByName(searchTxt.text); }
You are not using the generated responder so you can delete it.
 
<s:CallResponder id="getEmployeesSummaryByNameResult"/>
Locate the click attribute defined for the searchBtn Button.
 
<s:Button id="searchBtn" click="searchBtn_clickHandler(event)" ... />
Run the application, enter an employee name of Smith, and click the Search button. When the Search button is clicked, a new service call is made to the server and the responder is populated with this new data so the data displayed in the List changes (see Figure 15). Depending upon your server, the search may be case-sensitive. You could modify the server-side class if you did not want it to be case-sensitive.
 
Search by employee lastname.
Figure 15. Search by employee lastname.
Note: In this example, you are calling a method on the server to perform the employee filtering. However, because you already returned all the employee records to the client, you could have just filtered the records locally instead. This would be more efficient because unnecessary calls to the server would be avoided.
 

 
Step 10: Use conditional logic to retrieve all or only some records.

Modify the Search Button click handler so that it calls getEmployeesSummaryByName() if the TextInput has a value and getEmployeesSummary() if it does not.
 
Your event handler should appear as shown here:
 
protected function searchBtn_clickHandler(event:MouseEvent):void { if(searchTxt.text!="") { getEmployeesSummaryResult.token = employeeService.getEmployeesSummaryByName(searchTxt.text); } else { getEmployeesSummaryResult.token = employeeService.getEmployeesSummary(); } }
Run the application. Search for Smith and view the results. Next, remove the search string and search. You should see all the employees listed again.
 

 
Step 11: Search employees on the TextInput enter event.

Change the name of the searchBtn_clickHandler() function to doSearch() and modify the click event in the Button tag accordingly. Add an enter event handler to the TextInput control and call the same doSearch() method, passing the event object. Modify doSearch() to receive an argument of type Event.
 
The doSearch() method is now invoked when the Search button is clicked.
 
<s:Button id="searchBtn" click="doSearch(event)" .../>
This same function is invoked when the user presses the Enter or Return key when the TextInput control has focus.
 
<s:TextInput id="searchTxt" width="100%" focusIn="searchTxt_focusInHandler(event)" text="Employee Name" enter="doSearch(event)"/>
The enter event broadcasts a FlexEvent. The click event broadcasts a MouseEvent. In order to use the same handler for both events, change the type of the event object argument to the super class Event.
 
protected function doSearch(event:Event):void
Run the application, enter Smith in the TextInput control, and press the Enter or Return key. The search results should be displayed just as when the Search button was clicked.
 
 
Step 12: Display a busy indicator when data is being retrieved from the server.
After the List, create an instance of the BusyIndicator class with an id of busyIndicator, a symbolColor of red, and its verticalCenter and horizontalCenter properties set to 0. Inside your functions, use the addElement() method to display the indicator when a data call is made to the server and the removeElement() method to remove it once data is returned. Run the application and see the indicator when you make data calls (see Figure 16).
 
The code for the new component should appear as shown here:
 
<s:BusyIndicator id="busyIndicator" verticalCenter="0" horizontalCenter="0" symbolColor="red"/>
Because you instantiated the BusyIndicator in MXML, it will be displayed when the application loads which is fine since you are going to immediately make a data call to the server. You also want it be displayed when later calls are made to the server:
 
protected function doSearch(event:Event):void { this.addElement(busyIndicator); (...) }
To remove the indicator, you need to first create a result handler for the getEmployeesSummaryResult responder:
 
<s:CallResponder id="getEmployeesSummaryResult" result="getEmployeesSummaryResult_resultHandler(event)"/>
Inside the result handler, remove the indicator:
 
protected function getEmployeesSummaryResult_resultHandler(event:ResultEvent):void { this.removeElement(busyIndicator); }
Run the application. You should see the busy indicator (see Figure 16). Because you are currently making calls to a local server, it appears only for a very short time. When you change the application to use a production server, however, data calls will take longer and it will be important to display the busy indicator as a visual cue to the user so they know they are waiting for something.
 
Show a busy indicator when calls are made to the server.
Figure 16. Show a busy indicator when calls are made to the server.
This application contains a single view that displays a list of employees. In the next tutorial, you create an additional view to display employee details.
 

 
Learn more

Refer to the following resources to learn more about this topic:
 
 
Documentation: Accessing Data with Flex
 
Documentation: Using Adobe ColdFusion 9
 
Documentation: Using Flex 4.5
 
ActionScript 3 Reference
 
Flex Developer Center