Archive for the ‘Adobe Flex’ Category

Add a combo box to a DataGrid with automatic sorting

Monday, August 31st, 2009

Here I am posting a solution for a problem I had to solve for one of my projects. I needed to add a combo box to one of the columns in a DataGrid. Not only that, based on the value of the combo box, the elements in the DataGrid should re-order.
To add the combo box to one of the columns we need to set that column as editable and we need to add an item editor(in this case an inline item editor):

<mx:itemEditor>
    <mx:Component>
 <mx:ComboBox  editable="false" dataProvider="{outerDocument.sequenceNumbers}">

 </mx:ComboBox>
    </mx:Component>
</mx:itemEditor>

To sort the data grid based on the column you need to apply a SortField to the
ArrayCollection that holds the elements of the DataGrid.

   /**
    * Sort list of questions by sequence
    */
   private function sortQuestions():void {
    var sort:Sort= new Sort();
       var sequence:SortField= new SortField("Sequence",false,false);
       sort.fields=[sequence];
       this.questions.sort=sort;
       this.questions.refresh();

   }

The full code:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="creationComplete()">
 <mx:Script>
  <![CDATA[
   import mx.collections.Sort;
   import mx.collections.SortField;
   [Bindable]
   public var sequenceNumbers:ArrayCollection;

   /**
    * Prepare the data
    */
   private function creationComplete():void {
    this.initSequenceNumbers();
    this.sortQuestions();
   }

   /**
    * Sort list of questions by sequence
    */
   private function sortQuestions():void {
    var sort:Sort= new Sort();
       var sequence:SortField= new SortField("Sequence",false,false);
       sort.fields=[sequence];
       this.questions.sort=sort;
       this.questions.refresh();

   }

   /**
    * We initialize the sequence numbers taking into account the number of questions
    */
   private function initSequenceNumbers():void {
    this.sequenceNumbers= new ArrayCollection();
    for (var i:int=0; i<questions.length; i++ ) {
     sequenceNumbers.addItem(i+1);
    }
   }
  ]]>
 </mx:Script>
      <mx:DataGrid width="300" rowCount="4" editable="true">
             <mx:ArrayCollection id="questions">
           <mx:Object>
              <mx:Sequence>1</mx:Sequence>
              <mx:Question>Who is the queen of England?</mx:Question>
           </mx:Object>

               <mx:Object>
              <mx:Sequence>3</mx:Sequence>
              <mx:Question>Who is the king of pop?</mx:Question>
           </mx:Object>

           <mx:Object>
              <mx:Sequence>2</mx:Sequence>
              <mx:Question>Who is the king of Spain?</mx:Question>
           </mx:Object>

         </mx:ArrayCollection>

             <mx:columns>
                 <mx:DataGridColumn id="sequenceColumn" dataField="Sequence" headerText="Sequence" editable="true" editorDataField="value" sortable="false" >
               <mx:itemEditor >
                   <mx:Component>
                       <mx:ComboBox  editable="false" dataProvider="{outerDocument.sequenceNumbers}"  >

                       </mx:ComboBox>
                   </mx:Component>
               </mx:itemEditor>
           </mx:DataGridColumn>
                 <mx:DataGridColumn dataField="Question" headerText="Question" editable="false" sortable="false"/>
             </mx:columns>
         </mx:DataGrid>
</mx:Application>



Click here to see the Combo box in a DataGrid example in action

Creating a countdown clock with Timer

Monday, August 31st, 2009

Are you trying to find a quick and easy way of creating a countdown clock in Adobe Flex. Don’t look any further. I’ll go straight to the point:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="creationComplete()">
 <mx:Script>
 <![CDATA[
  import mx.controls.Alert;
  [Bindable]
  private var n_seconds_left:int=30;
  private var timer:Timer;

  /**
   * Initialises the timer
   */
  private function creationComplete():void {
  this.timer= new Timer(1000,n_seconds_left);
                      timer.addEventListener(TimerEvent.TIMER,decrementSeconds);
  timer.start();
  }

  /**
   * Decrements the number of seconds left if it still is
   * bigger than 0. Otherwise stop the timer.
   *
   */
  private function decrementSeconds(event:TimerEvent):void {
   n_seconds_left--;
  }

  ]]>
 </mx:Script>
 <mx:Label text="Final Countdown: {n_seconds_left}" fontSize="18"/>
</mx:Application>

The constructor for the Timer class in Adobe Flex takes two arguments:

  1. delay- Time interval in milliseconds for generating a TimerEvent. In our example, we set that value as 1000 milliseconds=1 second as you would expect for a clock.
  2. count – number of times that a TimerEvent will be generated before a TimerEvent.TIME_COMPLETE is generated. In our example it was 30. This means that our countdown clock only runs for 30 seconds. If you don’t specify this value the Timer will run forever and the TimerEvent.TIME_COMPLETE will never be dispatched

If we don’t specify the second argument when creating a Timer we still can achieve the same results:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="creationComplete()">
 <mx:Script>
  <![CDATA[
  import mx.controls.Alert;
  [Bindable]
  private var n_seconds_left:int=30;
  private var timer:Timer;

  /**
   * Initialises the timer
   */
  private function creationComplete():void {
   this.timer= new Timer(1000);
   timer.addEventListener(TimerEvent.TIMER, decrementSeconds);
   timer.start();
  }

  /**
   * Decrements the number of seconds left if it still is
   * bigger than 0. Otherwise stop the timer.
   *
   */
  private function decrementSeconds(event:TimerEvent):void {
     if (this.n_seconds_left>0) {
   n_seconds_left--;
     }
     else {
        timer.stop();
      }
  }

  ]]>
 </mx:Script>
 <mx:Label text="Final Countdown: {n_seconds_left}" fontSize="18"/>
</mx:Application>

The biggest difference to the previous code is that we use Timer.stop() to interrupt the timer to prevent another TimerEvent from being called so that our countdown clock doesn’t go below 0.


Click here to see our countdown clock in action

Adobe Flex with Struts 2 using HttpService

Monday, August 31st, 2009

Using HttpService is the easiest way in which you can integrate Adobe Flex and Struts 2. As an example I will show you how you can create a registration form in Adobe Flex which calls a struts 2 action to create new users. Note that this example is very simple and for demonstration purposes. I am not adding any validation in flex and not even creating the users in a database. All I show you is how you can pass data between flex and struts.

First let me show you the steps you need to perform in Adobe Flex:

Step 1

Create the registration form in mxml:

<mx:Form label="Registration Form">
 <mx:FormHeading label="Registration Form">
 </mx:FormHeading>
 <mx:FormItem label="First Name">
  <mx:TextInput id="firstName">
  </mx:TextInput>
 </mx:FormItem>
 <mx:FormItem label="Last Name">
  <mx:TextInput id="lastName">
  </mx:TextInput>
 </mx:FormItem>
 <mx:FormItem label="Email">
   <mx:TextInput id="email">
  </mx:TextInput>
 </mx:FormItem>
 <mx:FormItem label="Username">
  <mx:TextInput id="username">
  </mx:TextInput>
 </mx:FormItem>
 <mx:FormItem label="Password">
  <mx:TextInput id="password" displayAsPassword="true">
  </mx:TextInput>
 </mx:FormItem>
 <mx:FormItem>
  <mx:Button label="Register" click="registerUser()"/>
 </mx:FormItem>
</mx:Form>

Step 2

Create the HttpService object:

<mx:HTTPService id="registerService" showBusyCursor="true" useProxy="false" url="register.action" resultFormat="e4x" method="POST" result="registerConfirmation(event)" fault="registerFailed(event)"/>
  • register.action is the Struts 2 action that registers the user
  • registerConfirmation(event) is the function that is called if the http service call is successful.
  • registerFailed(event) is the function that is called if an error occurs. For example if the server is down. The event object gives you access to any xml/html that might be returned upon the call of the action.
  • resultFormat specifies in which format you want to view the results. It can be text, xml or an object. There are 5 possible values for this field:
    • object – An XML object is returned and is parsed as a tree of ActionScript objects
    • xml – Returns literal XML in an XMLNode object
    • e4x – Returns literal XML that can be accessed using ECMAScript for XML(E4X) expressions.
    • flashvars – The result is text in flashvars format, value pairs separated by ampersands. Flex returns this result in an actionscript object
    • array – The result is XML that is returned in an Array even if there is only one top level node.

Step 3

Write the actionscript call to submit the form:

public function registerUser():void {
      var params:Object = { 'user.firstName': firstName.text,'user.lastName': lastName.text, 'user.email':email.text, 'user.password':password.text };
      this.registerService.send(params);
}

Step 4

Create registerConfirmation(). This method checks whether the registration was successful. We can know that by inspecting the XML that is returned.

private function registerConfirmation(event:ResultEvent):void {
        var xml:XML=XML(event.result);
 if (xml != null && xml.item == true) {
  mx.controls.Alert.show("Registration Successful!");
 }
 else {
  mx.controls.Alert.show("The registration was not successful.");
 }
 }}

Step 5

If we can’t call the action on the server because of some networking issue we also should give some feedback to the user.

/**
 * Display a message to the user explaining what went wrong
 */
private function registerFailed(event:FaultEvent):void {
 mx.controls.Alert.show(event.fault.message);
}

Now let’s create our Struts 2 Action:

Step 1

Create RegisterAction.java

package uk.co.spltech.web.actions;

import uk.co.spltech.beans.User;

import com.opensymphony.xwork2.ActionSupport;

/**
 * Creates a new user
 *
 * @author Armindo Cachada
 *
 */
public class RegisterAction extends ActionSupport {

 private User user;

 private Boolean success;

 public Boolean getSuccess() {
  return success;
 }

 public void setSuccess(Boolean success) {
  this.success = success;
 }

 public User getUser() {
  return user;
 }

 public void setUser(User user) {
  this.user = user;
 }

 /**
  * Registers a user
  *
  * @return
  */
 public String register() {
  System.out.println("Checking user");
  if ( this.getUser()!= null) {
   User u = this.getUser();
   if (u.getEmail()!= null && !u.getEmail().equals("") && u.getPassword()!= null
     && !u.getPassword().equals("") ) {
    System.out.println("Successfully registered user with email=" + u.getEmail());
    this.success=true;
   }
   else {
    this.success=false;
    System.out.println("Error registering user");
   }
  }
  else {
   this.success=false;
   System.out.println("Error registering user");
  }

  return SUCCESS;

 }

}

Step 2

Add action name=’register’ to struts.xml

<package name="user" extends="struts-default">
        <action name="register" method="register" class="uk.co.spltech.web.actions.RegisterAction">
           <result type="xslt">   <param name="exposedValue">{success}</param></result>
        </action>
</package>

Source for “Adobe Flex with Struts 2 using HttpService” Example


To compile this example you will need to copy the following libraries to the lib folder:

commons-fileupload-1.2.1.jar
commons-io-1.3.2.jar
commons-logging.jar
freemarker-2.3.13.jar
ognl-2.6.11.jar
struts2-core-2.1.6.jar
xwork-2.1.2.jar
struts2-convention-plugin-2.1.6.jar

Integrating Adobe Flex with Struts 2

Monday, August 31st, 2009

I have noticed a lot of people on the internet are searching for information on how to integrate Struts 2 with Adobe Flex. It is not as complicated as most think and there’s more than one way of doing it.

Ways in which you can integrate Adobe Flex and Struts:

1. Use HttpService in Adobe Flex to call actions in Struts. In Struts, instead of returning a jsp file change the return type of your action to XSLT. In this way the contents of your action are automatically converted to XML. You can use the XML result in Adobe Flex to determine the result of the action that you invoked using HttpService.
This technique is by far the easiest of the three.
The main advantages are:

  • It is quite simple to use compared to the other techniques
  • You get complete control on the information that you transfer between Adobe Flex and Struts

There are disadvantages though:

  • The more information you transfer between Flex and Struts the more code you have to write for parsing/converting/… data

You can easily use HttpService to register a user using a struts action or to authenticate a user. Or you can use HttpService to retrieve information from the database through struts. In my next post I will give you an example on how to implement a registration system using HttpService.

2. Use Soap Web Services

Use Soap WebServices to connect to the Struts 2 backend. If you use an EJB 3 compliant application server you can easily create a web service by adding the @WebService annotation to an EJB 3 bean interface.
If you don’t have an EJB 3 compliant application server we recommend you to use Apache Axis 2 or Enunciate. The advantage of Enunciate is that you can publish your service not only for use with SOAP, it also creates endpoints for REST, AMF and JSON. We will exemplify this approach in a later post.

The advantages of using Web Services are:

  • Automatic class generation: With a few mouse clicks and the WSDL that describes your web service, Flex generates all the classes needed to access your web service.
    This is done via the ported version of Apache Axis.
  • The conversion of data from/to XML is done automatically for you. You only deal with objects and method calls.

The major disadvantage of using web services are circular references. An example of a circular reference is one where the child node of an object references its parent.

A —-> B
B —-> A

You need to deal with this by using annotations or by manually deleting the circular reference. Otherwise you will get a nasty marshaling/unmarshaling exception when calling the web service.

3. Using BlazeDS, from Flex it is possible to invoke methods on java objects deployed in an application server. This is done using AMF a message protocol developed by Adobe. This is a similar method to using SOAP. The only difference is that all the information is transferred in binary format. Performance wise this is probably the best option available to integrate Java/J2EE and Adobe Flex. Read my post on Integrate Adobe Flex and JBOSS using BlazeDS for more information.

Sorting an ArrayCollection using SortField

Monday, August 31st, 2009

You can use ArrayCollection and SortField to sort a list of items based on one or more fields:

this.names= new ArrayCollection();
names.addItem({first:"John",last:"Travolta"});
names.addItem({first:"Miguel",last:"Nunes"});
    names.addItem({first:"Christina",last:"Aguilera"});
names.addItem({first:"Michael",last:"Jackson"});
var sort:Sort= new Sort();
sort.fields=[new SortField("last",true,true), new SortField("first",true,true)];
names.sort=sort;
names.refresh();

You can try the adobe flex example here. View source is enabled.

Uploading a file with Flex 3 and Struts 2

Monday, August 31st, 2009

In my previous post I taught you how to upload a file using Struts 2. In the example I gave you, both the presentation layer and the code that received the file were done in Struts 2.
But what if you want to upload a file via Adobe Flex? How can you do it?

The FileReference flex class allows you to upload files from a client to a server. In our case a server running Struts 2. FileReference shows a dialog box that allows the user to select which file to upload.
Enough of theory. Below is an example of the code that you can use to upload a file to a server running struts 2:

Step 1

First you need to create the dialog box that will allow the user to select a file to upload by clicking the button “Browse”.

<mx:VBox width="100%" left="10" top="10" >
   <mx:HBox>
       <mx:Label text="Image to upload:"/> <mx:TextInput disabledColor="black" enabled="false" id="fileName" width="200"  />
              <mx:Button width="80" label="Browse" click="browseFiles()" />
   </mx:HBox>
          <mx:Button id="uploadButton" label="Upload" enabled="false" click="upload()"/>
          <mx:ProgressBar labelPlacement="center"  source="{fileRef}" label="%3%%" minimum="0" maximum="100" color="blue" height="13" width="100" id="progressBar" visible="false" includeInLayout="false" />

        </mx:VBox>

Step 2

Create an object of type FileReference and add event listeners to track progress of the upload:

 fileRef = new FileReference();
 fileRef.addEventListener(Event.COMPLETE, completeEvent);
 fileRef.addEventListener(Event.SELECT, selectEvent);
 fileRef.addEventListener(IOErrorEvent.IO_ERROR, showError);
 fileRef.addEventListener(ProgressEvent.PROGRESS, progressEvent);
 fileRef.addEventListener(SecurityErrorEvent.SECURITY_ERROR, showError);

Step 3

Write the actionscript code that opens a dialog that calls the native “Open File Dialog” in the user’s operating system.

private function browseFiles():void {
      var validFiles:Array= new Array(new FileFilter("Images (*.jpg,*.png,*.gif,*.jpeg)", "*.jpg;*.png;*.gif;*.jpeg"));
      this.fileRef.browse(validFiles);
   }

Step 4

Write the actionscript code that uploads the file to the server.
In our previous example the struts 2 action for the upload is “/fileUpload”.
Therefore we use the following url to upload the file: ${baseUrl}/fileUpload

/**
   * Uploads a file to the server
   *
   */
   private function upload():void {
     mx.controls.Alert.show("Starting upload.");
     var sendVars:URLVariables;
     var request:URLRequest;
     sendVars = new URLVariables();
     request = new URLRequest();
     request.data = sendVars;
     request.url =baseUrl + "/fileUpload";
     request.method = URLRequestMethod.POST;
     fileRef.upload(request,"file",false);
   }

You can find sample code to upload a file here. Note however that the example only works with a server to receive the file.

Adding an event listener function with arguments using Actionscript

Monday, August 31st, 2009

This might come handy whenever you can’t use directly mxml and you need to add an event listener function that needs one or more arguments.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="myInitialize()">
 <mx:Script>
  <![CDATA[
   import mx.controls.Alert;

      private function myFunction(arg:String):void {
       mx.controls.Alert.show("Received call with argument=" + arg);
      }

   private function myInitialize():void {
    myButton.addEventListener(MouseEvent.CLICK, function(event:MouseEvent):void{
     var someArg:String="an_argument";
     myFunction(someArg);
    });
   }
  ]]>
 </mx:Script>

 <mx:Button id="myButton" label="Click me!"/>
</mx:Application>

See this code in action here

Adobe Flex 3 UI Components Lifecycle

Monday, August 31st, 2009

In adobe flex 3 there are certain events that are called when a component is created.
These events are raised in the following order:

1) preInitialize: This event is raised when the component has just been created but none of the child components exist.

2) initialize: This event is raised after the component and all its children have been created but before any dimensions have been calculated.

3) creationComplete: This even is dispatched after all the component and its children have been created and after all the layout calculations have been performed.

4) applicationComplete: Dispatched after all the components of an application have been successfully created

Events 1) to 3) are dispatched in the order mentioned above for all visible components.
Note however that for components that are not visible on the screen by default, none of these events will be raised. This is because depending on the creation policy the hidden components might not exist yet.
For example in a TabNavigator only the components that are inside the default tab are created because they are visible. If you do change to another non-visible tab then Flex creates the components one by one. In that case the events mentioned above are raised.

We have created a simple example to demonstrate this. The DataGrid shows you a list of the events that have been raised during the lifecycle of the main application window. If you change to the second tab you will see additional entries in the list due to the creation of a Label UI component.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" preinitialize="preInitialise()" initialize="initialise()" applicationComplete="applicationComplete()" creationComplete="creationComplete()" viewSourceURL="srcview/index.html">
 <mx:Script>
  <![CDATA[
   import mx.collections.ArrayCollection;

   [Bindable]
   private var eventsCalled:ArrayCollection=new ArrayCollection();

   public function preInitialise():void {
    eventsCalled.addItem({label:"preInitialise called",event:"preInitialise"});
   }

   public function initialise():void {
    eventsCalled.addItem({label:"initialise called",event:"initialise"});
   }

   public function creationComplete():void {
    eventsCalled.addItem({label:"creation complete called",event:"creationComplete"});
   }

   public function applicationComplete():void {
    eventsCalled.addItem("applicationComplete called");
   }

  ]]>
 </mx:Script>
<mx:TabNavigator width="400" height="400">
  <mx:Canvas label="tab1">
   <mx:DataGrid id="datagrid" dataProvider="{eventsCalled}" top="10" left="10" width="300" />
  </mx:Canvas>
  <mx:Canvas label="tab2">
   <mx:Label text="This label is created only when it is displayed" creationComplete="creationComplete();" preinitialize="preInitialise();"  initialize="initialise();" />
  </mx:Canvas>

 </mx:TabNavigator>
</mx:Application>

To see this example in action click: here

You can change this behavior by setting the creationPolicy attribute, for example in the application mxml node. This attribute is only valid for containers.

There are 4 possible values for this attribute:

1) creationPolicy=”auto” – the container delays creating some or all of its descendants until they are needed. This is known as deferred instantiation

2) creationPolicy=”all” – the container immediately creates all its descendants even if they are not selected.

3) creationPolicy=”queued” – all components inside a container are added to a creation queue. The application waits until all children of a container are created before moving to the next container.

4. creationPolicy=”none” – all components inside a container are never created. Whoever decides to go with this strategy needs to manually create all items inside a container.

For example to change the behavior seen above we could set the creationPolicy=”all”:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationPolicy="all" preinitialize="preInitialise()" initialize="initialise()" applicationComplete="applicationComplete()" creationComplete="creationComplete()" viewSourceURL="srcview/index.html">
...
</mx:Application>