Adding Drag and Drag functionality in Adobe Air

In this tutorial I will teach you step by step how to use the out of the box drag & drop functionality provided by flex list controls.

Just some basic theory before we go into code. The drag & drop process has three stages – initiation, dragging and dropping.

The initiation is when the user clicks on a flex component and keeps the button pressed. In this case the component that the user clicks on is the drag initiator. Any flex component that supports dragging will handle either the mouseDown() or mouseMove() event to initiate the drag & drop operation. The event handler will then create a DragSource object which contains data related to the object being dragged. For example if you are dragging a file between two components, the DragSource object will contain a reference to a File object. Once the DragSource is constructed the DragManager.doDrag() method is called where the first argument is a reference to the object that initiated the drag, the second argument is the DragSource object and the third argument is a reference to the mouse event that called the event handler. There is a fourth optional argument that allows to specify a drag proxy that may be used to represent the object being dragged on the screen(e.g. a semi-transparent version of the drag initiator)

In the Dragging stage the user moves the flex component across the screen up to the drop target. If you specify a drag proxy image, that is what is shown on the screen when dragging the component. If not specified a rectangle will be shown instead.

In the dropping stage when the component being dragged arrives to a potential drop target, a dragEnter event is raised. The event handler that handles this event should check whether the DragSource object contains data that is of an acceptable format. If that’s the case then DragManager.acceptDragDrop() is called indicating that the drop target is happy in accepting the dragged data.

In this tutorial I will teach you step by step how to use the out of the box drag & drop functionality provided by flex list controls.

Just some basic theory before we go into code. The drag & drop process has three stages – initiation, dragging and dropping.

The initiation is when the user clicks on a flex component and keeps the button pressed. In this case the component that the user clicks on is the drag initiator. Any flex component that supports dragging will handle either the mouseDown() or mouseMove() event to initiate the drag & drop operation. The event handler will then create a DragSource object which contains data related to the object being dragged. For example if you are dragging a file between two components, the DragSource object will contain a reference to a File object. Once the DragSource is constructed the DragManager.doDrag() method is called where the first argument is a reference to the object that initiated the drag, the second argument is the DragSource object and the third argument is a reference to the mouse event that called the event handler. There is a fourth optional argument that allows to specify a drag proxy that may be used to represent the object being dragged on the screen(e.g. a semi-transparent version of the drag initiator)

In the Dragging stage the user moves the flex component across the screen up to the drop target. If you specify a drag proxy image, that is what is shown on the screen when dragging the component. If not specified a rectangle will be shown instead.

In the dropping stage when the component being dragged arrives to a potential drop target, a dragEnter event is raised. The event handler that handles this event should check whether the DragSource object contains data that is of an acceptable format.  If that’s the case then DragManager.acceptDragDrop() is called indicating that the drop target is happy in accepting the dragged data.

Fortunately for list controls most of this work is already done for you. Implementing drag & drop is very simple:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="horizontal">
     <mx:ArrayCollection id="source">
            <mx:Array>
            <mx:Object singer="Michael Jackson" />
            <mx:Object singer="Jennifer Lopez" />
            <mx:Object singer="Janet Jackson" />
            <mx:Object singer="Paul Mcarthney" />
        </mx:Array>
    </mx:ArrayCollection>

   <mx:ArrayCollection id="destination">
      <mx:Array>
          <mx:Object singer="Sheryl Crowe"/>
      </mx:Array>
   </mx:ArrayCollection>
   <mx:DataGrid dataProvider="{source}" dragEnabled="true" dragMoveEnabled="true"/>
   <mx:DataGrid dataProvider="{destination}" dropEnabled="true"/>
</mx:Application>

In the example above by setting dragEnabled to <b>true</b> allows the rows inside the data grid to be dragged. For any dragged rows to be accepted by the other data grid we need to set dropEnabled to true on that target DataGrid. We also set dragMoveEnabled to true to enable moving rows between data grids.

Ok, now let’s see how we can add drag & drop support to our file system manager. The steps required to do this are:

Step 1

We need to set the dragEnabled and the dropEnabled properties to true on both FileSystemDataGrid. The reason we do on both is because we want to allow the user to copy or move files in both directions: from left to right and right to left.

Step 2

We need to override the default handler for the DragEvent.DRAG_DROP event. This is because the standard drag & drop behavior for the FileSystemDataGrid isn’t quite what we need. In this event handler we need to call event.preventDefault() so that the default event handler is not called. The event handler inspects what file is specified inside the DragSource object carried by the event and performs the copy/move operation as specified by the key that user pressed. If the user just drags a file between the two data grids while pressing the shift key a move file operation is performed. Otherwise the default is to do a copy operation. If the file operation goes is successful we refresh the contents of two FileSystemDataGrid’s, otherwise an alert box is shown.

Here is the code:

 /**
  * Determines what object was dropped in the data grid
  */
 private function handleDragDrop(event:DragEvent):void {
 	// we do this because we want to override the default drag and drop
 	// behaviour implemented in the DataGrid
 	event.preventDefault();
 	var dragSource:DragSource=event.dragSource;
 	if (dragSource.hasFormat("items")) {
 		var items:Array = dragSource.dataForFormat("items") as Array ;
 		for each (var item:File in items) {
 			// copy this file to the directory indicated by fs2
 			// make sure that the origin data grid is not the same as the dest
 			if (event.dragInitiator!= event.target) {
 				var fileDest:File = FileSystemDataGrid(event.target).directory.resolvePath(item.name)

 				var dataGrid:FileSystemDataGrid=event.currentTarget as FileSystemDataGrid;
 				item.addEventListener(Event.COMPLETE, refreshFileSystems);
 				// if the user has pressed the shift key then he just
 				// wants to copy the file
 				if (event.shiftKey)
 				{
 					this.copyFile(item, fileDest);

 				}
 				else {
 					this.moveFile(item, fileDest);
 				}

 			}
 		}
 	}
 }

Click here to download the File System Manager Air app with Drag and Drop support


It is very cool to drag and drop a file from one file system data grid to another. However wouldn’t it be even cooler to be able to drag and drop files from the operating system desktop to Air file manager? That kind of drag & drop requires native drag & drop support. If you are interested in native drag and drop support then read my post on Native Drag and Drop in Adobe Air with example

The DisplayObject Blend Mode

The blend mode controls what happens when one display object is on top of another. The default setting in Adobe Flex is that the pixels of a display object override those that are displayed in the background. However you can change this behavior by modifying the blendMode setting. Read the language reference entry for DisplayObject. There you will find a detailed description of each blend mode.
In a nutshell the different blend modes available are:

  • BlendMode.NORMAL
  • BlendMode.LAYER
  • BlendMode.MULTIPLY
  • BlendMode.SCREEN
  • BlendMode.LIGHTEN
  • BlendMode.DARKEN
  • BlendMode.DIFFERENCE
  • BlendMode.DARKEN
  • BlendMode.OVERLAY
  • BlendMode.ADD
  • BlendMode.SUBTRACT
  • BlendMode.INVERT
  • BlendMode.ALPHA
  • BlendMode.ERASE
  • BlendMode.SHADE

Use the following flex code to try the different blend modes:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:core="*" xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" height="600" width="600" horizontalAlign="left">
 <mx:ArrayCollection id="blendModes">
  <mx:Array>
   <mx:Object label="Normal" data="normal"/>
   <mx:Object label="Layer" data="layer"/>
   <mx:Object label="Multiply" data="multiply"/>
   <mx:Object label="Screen" data="screen"/>
   <mx:Object label="Lighten" data="lighten"/>
   <mx:Object label="Darken" data="darken"/>
   <mx:Object label="Difference" data="difference"/> 
   <mx:Object label="Overlay" data="overlay"/>
   <mx:Object label="ADD" data="add"/>
   <mx:Object label="Subtract" data="subtract"/>
   <mx:Object label="Invert" data="invert"/>
   <mx:Object label="Alpha" data="alpha"/>
   <mx:Object label="Erase" data="erase"/>
  </mx:Array>
 </mx:ArrayCollection>
 <mx:Canvas height="200" width="400">
  <mx:VBox height="100" width="100" backgroundColor="{color1.selectedColor}" backgroundAlpha="1" blendMode="{blendMode1.selectedItem.data}" />
  <mx:VBox height="100" width="100" backgroundColor="{color2.selectedColor}" backgroundAlpha="1"  x="50" y="50" blendMode="{blendMode2.selectedItem.data}"/>
 </mx:Canvas>
 <mx:Panel title="Blend Modes" layout="horizontal">
  <mx:ComboBox id="blendMode1" dataProvider="{blendModes}" />
  <mx:ComboBox id="blendMode2" dataProvider="{blendModes}"/>
 </mx:Panel>
 <mx:Panel title="Colors">
  <mx:ColorPicker id="color1"/>
  <mx:ColorPicker id="color2" selectedColor="blue"/>
 </mx:Panel>
</mx:Application>




Click here to try the example.

Integrate Adobe Flex and JBOSS using BlazeDS – Part II

In the first part of this tutorial I showed you an example on how to integrate flex with JBOSS using BlazeDS. In this example we used RemoteObject in Actionscript to create a new instance of uk.co.spltech.remote.RemoteService and call the callMe() method remotely. The method returned a list of strings that we used to populate a datagrid.
In the second part of this tutorial we will do something slightly more useful. This time we will be invoking an EJB 3.0 bean managed by the application server.

Step 1

To be able to invoke EJB 3.0 from flex you need to download and copy FlexEJBFactory.jar to $JBOSS_HOME/server/default/lib. Or you can copy it to a local directory in your project separate from JBOSS e.g. example7/jbosslib and add the following line to $JBOSS_SERVER/server/default/conf/jboss-service.xml:

<classpath codebase="/usr/java/eclipse/workspace/example7/jbosslib" archives="*"/>

Previously all the BlazeDS jars were located in the WAR:WEB-INF/lib directory. But this is not going to work now because we need to see the jars within the context of the entire EAR. For that reason we need to move all those jars to to $JBOSS_SERVER/server/default/lib(or to a local directory inside your project e.g. jbosslib):

flex-ejb-factory.jar
flex-messaging-common.jar
flex-messaging-core.jar
flex-messaging-opt.jar
flex-messaging-proxy.jar
flex-messaging-remoting.jar
backport-util-concurrent.jar

Step 2

Because we will be invoking EJB 3.0 components we need to change the build process so that it creates an EAR file instead of a WAR. We add the following lines to the createjars target:

<jar jarfile="build/example7.jar">
 <fileset dir="${build.classes.dir}">
    <include name="uk/co/spltech/remote/*.class" />
 </fileset>
       
    </jar>

    <zip zipfile="build/example7.ear">
 <zipfileset dir="build">
  <include name="example7.war" />
 </zipfileset>
 <zipfileset dir="${build.dir}/resources" prefix="META-INF">
  <include name="application.xml" />
 </zipfileset>
       <zipfileset dir="build">
  <include name="example7.jar" />
 </zipfileset>
     </zip>

Step 3

Add the new ejb3 factory to flex/services-config.xml:

<factories>
     <factory id="ejb3" class="com.adobe.ac.ejb.EJB3Factory"/>
 </factories>

Step 4

Inside the uk.co.spltech.remote package create the following classes:

package uk.co.spltech.remote;

import java.util.List;

/**
 * This is an example of an interface where you can
 * declare all the methods that you want to call remotely
 * 
 * @author Armindo Cachada
 *
 */
public interface RemoteService {
 /**
  * I am not doing anything useful except to just show that I can be invoked remotely
  * from Adobe Flex using RemoteObject.
  * 
  */
 public List<String> callMe();
}

And the implementation of the interface:

package uk.co.spltech.remote;

import java.util.ArrayList;
import java.util.List;

import javax.ejb.Local;
import javax.ejb.Stateless;

/**
 * This remote service is called using BlazeDS/LiveCycle DS from Flex
 * 
 * @author Armindo Cachada
 *
 */
@Stateless(name = "remoteService")
@Local(RemoteService.class)
public class RemoteServiceImpl implements RemoteService {

 
 /**
  * I am not doing anything useful except to just show that I can be invoked remotely
  * from Adobe Flex using RemoteObject.
  * 
  */
 public List<String> callMe() {
  System.out.println("I was invoked!");
  List<String> result = new ArrayList<String>();
  result.add("Michael Jackson");
  result.add("Beatles");
  result.add("Tina Turner");
  return result;
 
 }
}

Step 5

Add the new destination to flex/remoting-config.xml:

<destination id="remoteService" >
        <properties>
              <factory>ejb3</factory>
              <source>example7/remoteService/local</source>
              <scope>application</scope>
         </properties>
   </destination>

There are two differences in the destination declaration:

  • The factory node indicates that we want to use the EJB3Factory declared in services-config.xml
  • The source instead of referencing a class name is changed to reference an EJB3 bean via JNDI.

Step 6

We are going to use the same mxml code as the one for Part I of this tutorial but we need to change the flex compiler settings:

-services "/usr/java/eclipse/workspace/example7/resources/flex/services-config.xml" -context-root "/example7" -locale en_US

After you have followed all these steps you should be good to go!




Download source code for JBOSS+Adobe Flex+BlazeDS project Part II

To save bandwidth I didn’t include all the jars needed:

flex-ejb-factory.jar
flex-messaging-common.jar
flex-messaging-core.jar
flex-messaging-opt.jar
flex-messaging-proxy.jar
flex-messaging-remoting.jar
backport-util-concurrent.jar

Copy these jars to the jbosslib directory inside the project. Don’t forget to change jboss-service.xml to reference this directory. Otherwise nothing will work.

Good luck!

How to use verticalCenter and horizontalCenter in a container with absolute layout

In this tutorial I will explain how you can use the verticalCenter and horizontalCenter constraints in a container with absolute layout.

At this stage you probably already figured out how to use the left, right, top, bottom constraints. But what about horizontalCenter and verticalCenter?

horizontalCenter is the distance from the center of the container in the horizontal where you want your component to appear. If you give it a positive value, the component will show up in the right half of the container. If you give it a negative value the component will appear in the left half.

<mx:Canvas width="500" height="500">
   <mx:Button horizontalCenter="200" label="Right half of the container"/>
   <mx:Button horizontalCenter="-200" label="Left half of the container"/>
</mx:Canvas>

If you try to combine horizontalCenter with left or right constraints, they will always be ignored as the horizontalCenter constraint always takes precedence.

<mx:Canvas width="500" height="500">
   <mx:Button horizontalCenter="200" left="400" label="Button 1"/>
   <mx:Button horizontalCenter="200" right="200" label="Button 2"/>
</mx:Canvas>

In the example above the two buttons will overlap because the value of the left and right coordinate is ignored.

verticalCenter is the distance from the center of the container in the vertical where you want your component to appear. If you give it a positive value, the component will show up in the bottom half of the container. If you give it a negative value the component will appear in the top half.

<mx:Canvas width="500" height="500">
   <mx:Button verticalCenter="200" label="Bottom half of the container"/>
   <mx:Button verticalCenter="-200" label="Top half of the container"/>
</mx:Canvas>

If you try to combine verticalCenter with top or bottom constraints, they will always be ignored as the verticalCenter constraint always takes precedence.

<mx:Canvas width="500" height="500">
   <mx:Button verticalCenter="200" top="400" label="Button 1"/>
   <mx:Button verticalCenter="200" bottom="200" label="Button 2"/>
</mx:Canvas>

In the example above the two buttons will overlap because the value of the top and bottom constraint is ignored.

The code below shows possible combinations of horizontalCenter and verticalCenter that you can use to layout your components:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" height="520" width="520">
<mx:Button label="Left Center" horizontalCenter="-200" verticalCenter="0">
</mx:Button>

<mx:Button label="Center" horizontalCenter="0" verticalCenter="0">
</mx:Button>

<mx:Button label="Right Center" horizontalCenter="200" verticalCenter="0">
</mx:Button>


<mx:Button label="Top Left" horizontalCenter="-200" verticalCenter="-200">
</mx:Button>

<mx:Button label="Top Center" horizontalCenter="0" verticalCenter="-200">
</mx:Button>

<mx:Button label="Top Right" horizontalCenter="200" verticalCenter="-200">
</mx:Button>

<mx:Button label="Bottom Left" horizontalCenter="-200" verticalCenter="200">
</mx:Button>

<mx:Button label="Bottom Center" horizontalCenter="0" verticalCenter="200">
</mx:Button>

<mx:Button label="Bottom Right" horizontalCenter="200" verticalCenter="200">
</mx:Button>


<mx:Button label="Ignored right constraint" right="10" horizontalCenter="-200" verticalCenter="50">
</mx:Button>

<mx:Button label="Not ignored right constraint" right="10" verticalCenter="50">
</mx:Button>

<mx:Button label="Ignored top constraint" top="10" horizontalCenter="-200" verticalCenter="-50">
</mx:Button>

<mx:Button label="Not Ignored top constraint" top="10" horizontalCenter="-200">
</mx:Button>

</mx:Application>




Click here to try the example above

Add a combo box to a DataGrid with automatic sorting

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

Adobe Flex with Struts 2 using HttpService

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

Sorting an ArrayCollection using SortField

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

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

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