Wednesday, March 28, 2007

The Flex Crash Course

Topics

What the heck are you talking about?

To be able to make sense of this you must have an understanding of software engineering, software architectural patterns, enterprise frameworks, and web programming. In particular an understanding of the either the .NET framework and/or J2EE (Java 2 Enterprise Edition) will be extremely helpful when considering integration strategies. The two involved architectural patterns are the 3-tier (http://en.wikipedia.org/wiki/Multitier_architecture) and the MVC (http://en.wikipedia.org/wiki/Model-view-controller). General knowledge of the internet and of XML (http://en.wikipedia.org/wiki/XML) are also required.

What is Flex?

Adobe® Flex™ 2 software is a rich Internet application framework based on Adobe Flash® that will enable you to productively create beautiful, scalable applications that can reach virtually anyone on any platform. It includes a powerful, Eclipse™ based development tool, an extensive visual component library, and high-performance data services enabling you to meet your applications' most demanding needs. (http://www.adobe.com/products/flex/)

Flex is basically an XML based language (called MXML), that also can incorporate scripting (ActionScript) in order to build graphical user interface applications in Flash. When considering the typical 3-tier model of the web enterprise, consisting of graphical user interface layer, the business rules layer, and the database layer, Flex applications reside in the graphical user interface layer. The power of Flex is its ability to seamlessly connect to the business rules layer through various service related objects. Flex is also able to integrate just as easily into the Model-View-Controller (MVC) architecture.

How do I get Flex?

You can download the free SDK from the Adobe website at http://www.adobe.com/products/flex/, as well as a 30-day trial of the Flex Builder IDE.

What do I need to run a Flex application?

Adobe Flash Player 9 is required to run Flex applications, which can be obtained for free from http://www.adobe.com/downloads/.

I have the SDK, how to I build a Flex application?

First (if you are running Windows XP) you need to make sure you have your added the location of the Flex SDK to your path. To do this right-click on My Computer, from the System Properties window select the Advanced tab, and press the Environment Variables button. In the Environment Variables window look in the System Variables section, and double click on the Path variable. On the end of the value for the variable, append the location of the bin directory of the location of where the flex SDK was installed preceded by a semicolon. For example my Path variable has the value %SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;C:\Program Files\ATI Technologies\ATI Control Panel;C:\Program Files\Java\jdk1.5.0_07\bin;C:\flex_sdk_2\bin, where C:\flex_sdk_2\bin is the location of the Flex SDK.

Adobe provides a tutorial at http://www.adobe.com/devnet/flex/quickstart/coding_with_mxml_and_actionscript/ that explains how write and build your first Flex application. To summarize:

1. Create a file called MyFirst.mxml and enter the following text:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
 horizontalAlign="center" verticalAlign="center">

 <mx:Button id="myButton" label="I'm a button!" />
</mx:Application> 

2. Open the command prompt (in Windows) by going to the Start menu, selecting Run..., typing cmd, and pressing the OK button.

3. In the command prompt window navigate the location that you created the file, and type the following:

mxmlc --strict=true --file-specs MyFirst.mxml

4. A file called MyFirst.swf will then be created in the same directory as the mxml file.

5. If you double-click on MyFirst.swf the application will open in a Flash Viewer, or you can open the file in a web browser to see the application.

What other GUI components are available and how do I use them?

Adobe provides a basic visual component tutorial at http://www.adobe.com/devnet/flex/quickstart/using_controls/, and an advanced visual component tutorial at http://www.adobe.com/devnet/flex/quickstart/using_the_repeater/.

How do I embed a Flex application in a web page?

You have to use both the object and embed tags in HTML in order for the application to display in the various types of web browsers. For example you would use the following code inside of an HTML page to display the one button Flex application:

<object codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0' 
    width='100%' height='100%'>
    <param name='src' value='MyFirst.swf'>
    <embed pluginspage='http://www.macromedia.com/go/getflashplayer' 
    width='100%' height='100%' src='MyFirst.swf'/>
</object>

How do I access external content in my Flex application?

There are numerous ways to access external content from within a Flex application, the easiest way is to access XML data through the HTTPService object.

Consider the following XML file named httpservice.xml:

<?xml version="1.0" encoding="utf-8"?>
<structure>

 <name>Generic Object</name>

 <attributes>
  <attribute>
   <name>A</name>
   <value>4</value>
  </attribute>

  <attribute>
   <name>B</name>
   <value>6</value>
  </attribute>
 </attributes>

</structure>

To access the data within httpservice.xml,  declare the following HTTPService object in httpservice.mxml:

<?xml version="1.0" ?>

<!-- Defines the application, and to obtain the data from 'httpsettings.xml when the application is 
     done being constructed -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*" creationComplete="xmlData.send()">

 <!-- Define the service used to connect to the xml file -->
 <mx:HTTPService id="xmlData" url="httpservice.xml" useProxy="false" />

</mx:Application>

Now build the application using mxmlc --strict=true --file-specs httpservice.mxml, and httpservice.swf will be created. This application will now read from the XML file, but it is not doing anything with it. A useful component for visualizing XML data is the DataGrid, as show in a modified version of httpservice.mxml:

<?xml version="1.0" ?>

<!-- Defines the application, and to obtain the data from 'httpsettings.xml when the application is 
     done being constructed -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*" creationComplete="xmlData.send()">

 <!-- Define the service used to connect to the rss feed -->
 <mx:HTTPService id="xmlData" url="httpservice.xml" useProxy="false" />

 <!-- Create a grid that displays all of the attribute elements within the xml data file -->
 <mx:DataGrid id="entries" width="300" height="100" 
  dataProvider="{xmlData.lastResult.structure.attributes.attribute}">
  <mx:columns>
   <!-- The 'name' dataField refers to the name tag in the attribute structure -->
   <mx:DataGridColumn dataField="name" headerText="Name" />
   <!-- The 'value' dataField refers to the value tag in the attribute structure -->
   <mx:DataGridColumn dataField="value" headerText="Value" />
  </mx:columns>
 </mx:DataGrid>

</mx:Application>

The dataProvider attribute of the DataGrid specifies the object that contains the data elements to display, and the DataGridColumn objects allow you to define the names of the columns and the data elements to be displayed within them relative to the parent data element. The parent data element is structure.attributes.attribute, of which the relative child data elements are name and value. Building the new httpservice.mxml into httpservice.swf now looks like the following when run:

Flex Objects:

 

The RSS Reader: How do I do something interesting with XML based external content?

Pete Frietag made a tutorial at (http://www.petefreitag.com/item/490.cfm) on how to create an RSS Reader in Flex, but it was done in Flex 1.5 and doesn't entirely work for Flex 2.0. The following code updates Pete's work for Flex 2.0, and uses the Pete's RSS feed located at http://www.petefreitag.com/rss/.

<?xml version="1.0" ?>

<!-- Defines the position of the application, and calls the RSS service on form completion -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*" 
 horizontalAlign="left" verticalAlign="top" creationComplete="httpRSS.send()">

 <!-- Define the service used to connect to the rss feed -->
 <mx:HTTPService id="httpRSS" url="http://www.petefreitag.com/rss/" useProxy="false" />

 <!-- This panel contains the data grid and the text area -->
 <mx:Panel id="reader" title="RSS 2.0 Reader" width="100%" height="100%">

 <!-- Create a data grid using the RSS Channel Item -->
 <mx:DataGrid id="entries" width="100%" height="30%" dataProvider="{httpRSS.lastResult.rss.channel.item}">
  <mx:columns>
   <mx:DataGridColumn dataField="title" headerText="Title" />
   <mx:DataGridColumn dataField="pubDate" headerText="Date" />
  </mx:columns>
 </mx:DataGrid>

 <!-- This is the text area where the selected article is displayed -->
 <mx:TextArea id="body" editable="false" width="100%" height="70%" 
  condenseWhite="true" htmlText="{entries.selectedItem.description}"/>

 </mx:Panel>

</mx:Application>

This code produces the following Flex Application:

When an item is selected within the DataGrid, that text content from that item is displayed in the TextArea. This is because the htmlText attribute of the TextArea is set to entries.selectedItem.description.

Flex Objects:

When I try and access an RSS feed, I get the error: RPC Fault faultString="Security error accessing url" faultCode="Channel.Security.Error". What is going on?

I got this answer from http://www.cflex.net/flexcoders.cfm?Message=msg26373.html.

In order for you to be able to access an RSS feed through Flex, there must be a file called crossdomain.xml in the root web directory of the domain that you are trying to access, which specifies the domains that are allowed to connect.

If you wanted to allow others to use your content on your domain, you would have to upload your own crossdomain.xml file. The following is an example:

<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy 
  SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
 <allow-access-from domain="www.site1.com" />
 <allow-access-from domain="site2.com" />
</cross-domain-policy>

This file should then be uploaded to http://www.yoursite.com/crossdomain.xml, and would allow the two listed sites to access content on your domain.

How do I send data from Flex?

Just as in accessing external content in Flex, there are several ways to send data to the rest of the web enterprise. One of the easiest ways is to use the HTTPService object again. This object be set to take XML parameters from within the Flex application and POST them to some other URL, ie a URL containing some server side logic that handles POST data like a  JSP page, ASP page, .NET page, Servlet, etc.

The following is the mxml for a Flex login application that sends the username and password as a POST to login.asp:

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*" 
    horizontalAlign="left" verticalAlign="top">

 <!-- Create a service that is used to send the given username and password to the login service -->
 <mx:HTTPService id="loginService" url="login.asp" 
     useProxy="false" method="POST">
  <mx:request xmlns="">
   <username>{username.text}</username><password>{password.text}</password>
  </mx:request>
 </mx:HTTPService>

 <mx:Panel layout="vertical" title="Example Login"
     width="250" height="160">

 <mx:Form>

  <mx:FormItem label="Username">
   <mx:TextInput id="username" width="100" />
  </mx:FormItem>

  <mx:FormItem label="Password">
   <mx:TextInput id="password" width="100" displayAsPassword="true" />
  </mx:FormItem>

  <mx:Button id="loginButton" label="Login" click="loginService.send();"/>

 </mx:Form>

 </mx:Panel>

</mx:Application>

The HTTPService on send() is set to send the username and password as specified in the username and password TextInputs as a POST to login.asp. The login service needs to return something back to Flex though, which can be done through the same HTTPService object. Consider the following ASP page, which returns whether the given posted username and password is correct through XML:

<!--
This service receives a username and password from
a POST and returns an xml statement that specifies
whether the given username and password is valid.
Output format:
<response>
 <username>Given Username</username>
 <password>Given Password</password>
 <valid_user>true|false</valid_user>
</response>
-->
<%
Response.ContentType="text/xml"

Dim username, password

'Get the posted username and password from Flex'
username = Request.Form("username")
password = Request.Form("password")


'This is were you would connect to your database'
'and determine if the given username and password is valid'
'......'

'This is where you generate the xml response'
Response.Write("<response>" & vbCrLf)
Response.Write(vbTab & "<username>" & username & "</username>" & vbCrLf)
Response.Write(vbTab & "<password>" & password & "</password>" & vbCrLf)
Response.Write(vbTab & "<valid_user>false</valid_user>" & vbCrLf)
Response.Write("</response>" & vbCrLf)
%>

The desired XML output should look something like the following:

<response>
 <username>John1234</username>
 <password>mypassword</password>
 <valid_user>true</valid_user>
</response>

Normally you wouldn't pass around the username and password unnecessarily or unencrypted, but this is just done to show that the username and password are actually being received. In order to do something more useful in Flex, you will need to add some ActionScript to the login Flex application:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*" 
horizontalAlign="left" verticalAlign="top">

 <mx:Script>
  <![CDATA[
  import mx.controls.Alert;
  import mx.rpc.events.ResultEvent;

  /**
  * Called when the username and password is sent to the login service
  * and receives the data in the following format:
  * <response>
  * <username>Given Username</username>
  * <password>Given Password</password>
  * <valid_user>true|false</valid_user>
  * </response>
  */
  private function checkLogin(evt:ResultEvent):void {

   var validLogin:String = evt.result.response.valid_user;
   var user:String = evt.result.response.username;
   var pass:String = evt.result.response.password;

   //Display a message box
   Alert.show("Valid: " + validLogin + "\n" +
   "Username: " + user +"\n" +
   "Password: " + pass);
  }

  ]]>
 </mx:Script>

 <!-- Create a service that is used to send the given username and password to the login service 
 When the response is received the checkLogin function is called with the received xml data -->
 <mx:HTTPService id="loginService" url="login.asp" 
 useProxy="false" method="POST" result="checkLogin(event)">
  <mx:request xmlns="">
   <username>{username.text}</username><password>{password.text}</password>
  </mx:request>
 </mx:HTTPService>

 <mx:Panel layout="vertical" title="Example Login"
 width="250" height="160">

 <mx:Form>

  <mx:FormItem label="Username">
   <mx:TextInput id="username" width="100" />
  </mx:FormItem>

  <mx:FormItem label="Password">
   <mx:TextInput id="password" width="100" displayAsPassword="true" />
  </mx:FormItem>

  <mx:Button id="loginButton" label="Login" click="loginService.send()"/>

 </mx:Form>

 </mx:Panel>

</mx:Application>

This application looks the following with the username and password entered:

When the Login button is pressed the given username and password are sent to login.asp, and that page generates the XML that was previously specified, which is then sent back to the Flex application. The information is displayed in the following message box:

How do I iterate through XML data using ActionScript?

Unfortunately iteration through XML data doesn't work correctly when using the Object or ResultEvent objects. For example if you had the following XML structure:

<?xml version="1.0" encoding="utf-8"?>
<structure>

 <name>Generic Object</name>

 <attributes>
  <attribute>
   <name>A</name>
   <value>4</value>
  </attribute>

  <attribute>
   <name>B</name>
   <value>6</value>
  </attribute>
 </attributes>

</structure>

If this structure were contained in the ResultEvent object, you could iterate through the attribute elements like the following:

private function test(evt:ResultEvent):void {
 for (var i:int = 0; i < evt.result.structure.attributes.attribute.length; i++) {
  var name:String = evt.result.structure.attributes.attribute[i].name;
  var value:String = evt.result.structure.attributes.attribute[i].value; 
 }
}

There is a problem with doing this though, if there were only 1 attribute this code would detect 0 elements instead of 1. The solution is to convert the XML data to an array:

import mx.rpc.events.ResultEvent;
import mx.collections.ArrayCollection;

/**
 * Converts the given object to an array
 */
private function convertToArray(object:Object):Array {
 var array:Array;
 if(object is ArrayCollection)
  array = object.toArray();
 else
  array = [object];
 return array;
}

private function test(evt:ResultEvent):void {
 var array:Array = convertToArray(evt.result.structure.attributes.attribute);
 for (var i:Object in array) {
  var name:String = array[i].name;
  var value:String = array[i].value; 
 }
}

How do I pass parameters to a Flex application?

Parameters can be passed to a Flex application by using the flashVars variable within the HTML object/embed tag. For example the following HTML sends the variable settings and location with their values to the Flex application names test.swf:

<object codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0' width='100%' height='100%'>
 <param name='flashVars' value='settings=settings.xml&location=www.yoursite.com'>
 <param name='src' value='test.swf'>
 <embed pluginspage='http://www.macromedia.com/go/getflashplayer' width='100%' height='100%'
  flashVars='settings=settings.xml&location=www.yoursite.com' src='test.swf'/>
</object>

The values of variables are set using the equals sign, and groups of variables and their values are separated by ampersands.

Inside of the Flex application, the following shows the ActionScript used to access the given parameters:

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*" creationComplete="initSettings()">

 <mx:Script>
 <![CDATA[

 private function initSettings():void {
  var settings:String = Application.application.parameters.settings;
  var location:String = Application.application.parameters.location;
 }

 ]]>
 </mx:Script>
</mx:Application>