Accessibility

Trio Motor Sample Article

 

Building the Trio Service Manager: The Work Order Status Widget in Flash MX 2004


Table of Contents

Using the Flash MX 2004 Remoting ActionScript 2.0 API and Building the ServiceOrder Class

Here is where the excitement begins! Now you will develop the ServiceOrder class. You will gain experience working with the Flash MX 2004 Remoting ActionScript 2.0 API.

To create the ServiceOrder.as AS 2.0 class:

  1. Open the file ServiceOrders.as in the following location: <your_web_root>\mx2004tutorial\com\trioMotor. If you were to create an ActionScript class file from scratch, you would select File > New > ActionScript. Click OK.
  2. Import the appropriate classes necessary for your new class. In the script window, add the following code:

    import mx.controls.TextInput;
    import mx.controls.List;
    import mx.controls.ComboBox;
    import mx.controls.Button;
    

    To reference a class in another script, you must prefix the class name with the class's package path. This simplifies the syntax required when you create instances of the UI components in your ServiceOrder class.

  3. Import the ActionScript Flash Remoting classes by adding the code below. Note: Comments are optional; I have added them to provide additional explanation to the documentation.
    // Represents a reference to a specific remote service and the methods that the service exposes
    import mx.remoting.Service;
    
    // Generated on each call to a Service object
    import mx.remoting.PendingCall;
    
    // Accesses and manipulate RecordSet objects returned by a service
    import mx.remoting.RecordSet;
    
    // Binds RecordSet objects to Flash components, such as ListBox or ComboBox, that have labels with associated data
    import mx.remoting.DataGlue; 
  4. The following is a responder object that relays result and fault calls to corresponding functions on the specified object.
    // A responder object that relays result and fault calls to corresponding functions on the specified object
    import mx.rpc.RelayResponder;
    
    // Handle results from server
    import mx.rpc.ResultEvent;
    
    // To handle the error in your Flash application, you create a fault handler
    import mx.rpc.FaultEvent;
    
    

    Your code should look something like this (remember, comments are optional):

    import mx.controls.TextInput;
    import mx.controls.List;
    import mx.controls.ComboBox;
    import mx.controls.Button;
    
    import mx.remoting.Service;
    import mx.remoting.PendingCall;
    import mx.remoting.RecordSet;
    import mx.remoting.DataGlue;
    
    import mx.rpc.RelayResponder;
    import mx.rpc.FaultEvent;
    import mx.rpc.ResultEvent;
    
  5. Add the class definition as follows:

    class com.trioMotor.ServiceOrders {
    	private var  cfConnection:NetConnection;
    	private var  workOrders:RecordSet;	
    
    	// UI Components Instances
    	private var  serviceOrders:List;
    	private var  statusComboBox:ComboBox;
    	private var  submit:Button;
    	private var  license:TextInput;
    	private var  path:String;
    
    	/* Add constructor code will go here for the Step #7 or next step */
    	}
    
  6. You have now created the class definition. Important: Insert the code in the remaining steps above the closing curly brace in step five.

  7. Add the constructor for the ServiceOrders class. You pass in the connection string for your Flash Remoting Gateway from the FLA, as well as the component instance names from your view. You will also keep your constructor function uncluttered by adding an init() function.

    /*  Constructor example only: 
        var myServiceOrder:ServiceOrders = new ServiceOrders( path, statusCodes, servOrdersList, license_txt, submit);
     */ 
    	function ServiceOrders(p:String, cb:ComboBox, lb:List, txt:TextInput, btn:Button)
    	{
    		license = txt;
    		statusComboBox = cb;
    		serviceOrders = lb;
    		submit = btn;
    		path = p;
    		init();
    	}
    	
    
  8. The init() function adds listeners for the UI components.

    public function init():Void
    	{
    		/* listener for the TextInput*/
    		license.addEventListener("change", this);	
    
    		/* listener for the ComboBox*/
    		statusComboBox.addEventListener("change", this);
    
    		/* listener for the Button*/
    		submit.addEventListener("openServiceManager", this);
    
    		createConnection();
    	}
    	
    
  9. Define your service to the ColdFusion components. There is a Service object and Pending object, both of which are new to Flash Remoting. The documentation and syntax are provided below:

    Service Object

    new Service (gatewayURI: String, logger:Log, serviceName:String, conn:Connection, resp:Responder)

    Service Object
    Parameter Description
    gatewayURI A string specifying the gateway that should be created or used from the current pool
    logger A string specifying the log to which debugging messages are sent
    serviceName A string specifying the name of the service on which to invoke the method
    conn The Connection object with which this service should be associated
    resp A Responder object that handles the callbacks for the method calls that have been made

    Pending Call

    Each call to a Service object generates a PendingCall object. The PendingCall object is responsible for setting up the responder and managing the result and fault outcomes.

    Properties
    Property Access Description
    responder read/write Sets or returns a Responder object

    Example

    The following example sets the PendingCall responder property with the Responder object to use (this), the name of the result handler function (onCategoryData), and the name of the fault handler function (onCategoryFault).

    custService = new Service("http://localhost/flashservices/", null, "customerData", null, null);
    // load category combo
    var pc:PendingCall = custService.getCategories(); // get all categories
    pc.responder = new RelayResponder(this, "onCategoryData", "onCategoryFault");
    
    

    Code to Add

    Add the following code after the closing bracket for public function init():Void function you added in step 8.

    public function createConnection():Void
    	{
                 // Create a new Service instance. Set the gateway path property & the workOrder CFC location parameters.
    		var work_Orders = new Service(path, null, "mx2004tutorial.services.workOrder", null, null );
    
                 // In the CFC, call the getWorkOrders() method, which is returned to a new instance pendingWorkOrders.
                  // This method creates query object in ColdFusion and returns it to Flash as an Array of Objects. 
    		var pendingWorkOrders:PendingCall = work_Orders.getWorkOrders();
    
                 // Set the responder property to the filterWorkOrder result handler & the reportError error handler.
    		pendingWorkOrders.responder = new RelayResponder( this, "filterWorkOrders", "reportError" );
                 
                 // Create a new Service instance. Set the gateway path property & the statusCode CFC location parameters.
    		var status_Code = new Service(path, null, "mx2004tutorial.services.statusCode", null, null );
                  // Invoke the getStatusCodes() and return pendingstatusCode instance.
    		var pendingstatusCode:PendingCall = status_Code.getStatusCodes();
                  // Set the responder property to the fillComboBox result handler & reuse the reportError error handler.
    		pendingstatusCode.responder = new RelayResponder( this, "fillComboBox", "reportError" );
    	}
    	
    
  10. When the recordset has been received, the RelayResponder calls the fillComboBox method. You named the object r, which is a ResultEvent data type. The DataGlue class lets you bind recordsets to a component.

    public function fillComboBox(r:ResultEvent):Void
    	{
    	/* Populate the ComboBox with the returned recordSet using DataGlue.bindFormatString to remap 
    	fields so that label equals STATUS_NAME field and data equals STATUS_ID field.  A ColdFusion programmer 
          will be familiar with the #variable# syntax.
    	  example: DataGlue.bindFormatStrings( component, object, label, data );
          */
    	DataGlue.bindFormatStrings( statusComboBox, r.result, "#STATUS_NAME#", "#STATUS_ID#" );
    	}
    	
    
  11. Define a simple error-handling method using a trace statement that displays the error message. This is helpful during the debugging process. In the responder, you defined the reportError fault handler function, which is called if there is a problem.

    public function reportError(myFault:FaultEvent):Void
    	{
    		trace("reportError: " + myFault.fault.faultstring);
    	}
    
  12. This function returns a Boolean if the record needs to be filtered based on user input from the components.

    public static function FILTER_FUNCTION(record, obj ):Boolean
    {
    /*	Make the license text the same length of the obj.text length to compare them. */
    var licenseText:String = record.LICENSE.substr( 0, obj.txt.length );
    /*	Return true if both texts and ids are equal or if texts are equal and id is "All
    *	Active" (id = 1). Otherwise returns false. */
    return (licenseText == obj.txt && ( record.STATUS_ID ==  obj.id || obj.id == 1) )
    }
    
    
  13. This function populates the ListBox component with the service orders.

    public function filterWorkOrders(r:ResultEvent):Void
    {
    /* Keep a reference to the query returned casting to a recordset class*/
    if(r != undefined) workOrders = RecordSet(r.result);
    
    /* Clean your List, removing all current items. */
    serviceOrders.removeAll();
    
    /* This object will be passed to the filter function with two properties. */
    var filterParams:Object = {};
    
    /* Get the text typed in the TextInput and convert it to upper case. */
    filterParams.txt = license.text.toUpperCase();
    
    /*	Get the selected item from the ComboBox.
    *	If this is undefined we assign the value 1, which corresponds to the ID of "All
    *	Active".
    */
    filterParams.id = (statusComboBox.selectedItem.data != undefined) ? statusComboBox.selectedItem.data : 1;
    
    /* Apply the filter that returns a new RecordSet. */
    var rec:RecordSet = workOrders.filter(FILTER_FUNCTION, filterParams);
    
    /* Get the Iterator from this new filtered recordset and populate the List. */
    var it = rec.getIterator();
    while( it.hasNext())
    {
    var record = it.next();
    serviceOrders.addItem(record.LICENSE + " ( " +record.STATUS_NAME + " )", record.WORK_ORDER_ID );
    }
    }
    
    
  14. Whenever a user makes a selection in the ComboBox or updates the TextInput component, the application calls this method. You have registered a listener of these two components.

    public function change():Void
    {
    filterWorkOrders();
    }
    
  15. If the user clicks the submit button, the application calls this method. The user will be sent to the Service Manager application and the code preserves the state from the component and maintains the state of components using URL parameters. Note: You will build the Service Manager application in the next tutorial of the series.

    public function openServiceManager():Void
    { getURL("serviceManager.cfm?workOrder=" + serviceOrders.selectedItem.data  + "&license=" 
                         + license.text + "&status=" + statusComboBox.selectedItem.data);
    }
    
  16. Save your work. If you would like to compare your work to the solution, download and unzip your solution. Open the solution at <your_web_root>\mx2004solution\com\trioMotor\serviceOrders.as.