In the Introduction to Flash Remoting post, I explained how to connect to a Flash Remoting gateway. I believe that the most difficult part when using Remoting, however, is knowing how to manage the transfer of data from and to the server. This transfer happens when Flash makes a call to a service and sends parameters, and when the server sends a response (not a void function). I will first briefly explain how to send and receive any type of data and then go through all the data types available and show you how to handle them.
In the Introduction to Flash Remoting post, I explained how to connect to a Flash Remoting gateway. I believe that the most difficult part when using Remoting, however, is knowing how to manage the transfer of data from and to the server. This transfer happens when Flash makes a call to a service and sends parameters, and when the server sends a response (not a void function). I will first briefly explain how to send and receive any type of data and then go through all the data types available and show you how to handle them.
The simplest method to send parameters to a CFC function is by sending them in order, separated by commas inside the call parenthesis.
If we have the following CFC function:
We would call it by (get the data from some control, not hard coded like in this example):
var product_id = 462;
var quantity = 1;
var price = 10.50;
myService.addCartItem(product_id, quantity, price);
//or simply
myService.addCartItem(462, 1, 10.50);
But when our function requires a lot of parameters, this becomes tedious and error prone, since it’s very easy to mistakenly change the order of the parameters. A better way to send a lot of parameters is by sending an object (like an structure or hashmap) with key-value pairs that match our cfargument names:
var myArguments = {}; //or new Object();
myArguments.product_id = 462;
myArguments.quantity = 1;
myArguments.price = 10.50;
myService.addCartItem(myArguments);
//we could also use the abbreviated version:
myService.addCartItem({product_id: 462, quantity: 1, price: 10.50 });
Even another method is by using an “associative array” (very similar to the object method, but creating an array instead):
var myArguments = []; //or new Array();
myArguments["product_id"] = 462;
myArguments["quantity"] = 1;
myArguments["price"] = 10.50;
myService.addCartItem(myArguments);
There is no trick here. To send data back to Flash, just use the cfreturn tag:
Remember that the type of the data that you are sending to Flash must match the data type specified in the returnType attribute of the cffunction tag.
We access this data in our handler functions. If we use the Service object (see previous post), specifying the handler function as
new RelayResponder(this, "onCallReceived", "onFault");
we will get the results in our onCallReceived function, and the data will be inside the ResultEvent object in the property “result”.
function onCallReceived ( re:ResultEvent ):Void {
var myData = re.result ;
}
If we are using the old method, then the data is directly in the “results” object
responseHandler.onResult = function(results: Object ):Void {
//use the results
}
In that parameter, which we just called “results”, we receive whatever was sent by the server. If it was a string, for example, we will use it directly: myInput.text = results. Note that we can name this parameter however we want, it doesn’t have to be called “results” (same for the ResultEvent parameter in the Service example)
Strings and numbers are the easiest data types to use, as they are natively handled in Flash and CF. You can send data coming from a text input: myInput.text or anything that can be converted to a String by using the toString() function.
Numbers are a little more difficult just because CF is not strictly typed and Flash is. Sending numbers from Flash will always work, even if we send them as strings, as CF will be able to convert them, unless of course, you send other characters besides numbers in the string.
From our previous example, the first argument is numeric:
We can supply that argument by:
myService.addCartItem(3);
//Or
myService.addCartItem("3"); //not the best method
You can always replace that with a variable:
myService.addCartItem(productInput.text);
Returning a number is straight forward if we set the returnType of the function as numeric:
You can even return it as a string <cfreturn “3”/> and it will still work. If you do not specify the returnType or you specify it as “any” (not recommended), it will be received as a string, not as a number. To convert it to a number, use the val() function:
These behave in the same way as the numbers: from Flash to CF, anything goes, but from CF to Flash, they are all strings unless otherwise specified in the returnType attribute. Therefore, it is important to specify the returnType to boolean in our CFC function.
You can send any of these to a CF boolean argument (<cfargument name="active" type="boolean" required="true">)
myService.myFunction(true); //or false
myService.myFunction("yes"); //or "no"
myService.myFunction("true"); //or "false"
myService.myFunction(1); //or 0. For true, any non-zero value will do
All those will be accepted as boolean values by the function, and the argument can be used in boolean expressions as in
The same guidelines that I gave for numbers work for Booleans: always specify the returnType to boolean. But what happens if we want to set a boolean in an array and send that to Flash? We have a problem. They will be just strings, which means they will always evaluate to true when used in an expression: if (myReturnedValue) , which makes it a bug difficult to find.
So how do we ensure that Flash receives a boolean? Macromedia documentation says that we can send a 0/1, but that has never worked for me. The only way I found to make sure I am returning a boolean (if I am not returning the boolean directly so that I can specify the returnType), is to use the javacast function:
Arrays can be sent either way without any problems. CF will receive an array in an arguments like:
The array can come from many different sources, such as a control’s dataprovider. It can also be created:
var myArray = [1,2,3];
myService.addCartProducts(myArray);
The arrays can also contain complex objects inside that follow all the other rules discussed here.
If we want to return an array from ColdFusion, we can set the returnType attribute to array:
When we receive that on the Flash end, we just must remember that Flash array indices start at 0:
responseHandler.onResult = function( results: Object ):Void {
myInput.text = results[0];
//or loop over all the items received
for (var i = 0; i < results.length; i++){
//do something with the results[i] element
}
}
ActionScript associative arrays and simple objects can be sent to CF and they will be received as key-value structures. If we have an argument type struct in our CFC function:
we use the same technique as when passing many arguments in one object or array:
var cartItem = {}; //or new Object();
cartItem.product_id = 462;
cartItem.quantity = 1;
cartItem.price = 10.50;
Or
var cartItem = []; //or new Array();
cartItem ["product_id"] = 462;
cartItem ["quantity"] = 1;
cartItem ["price"] = 10.50;
So what’s the difference?
The main difference is that it cannot be the only argument supplied to the function. If we pass that as the sole argument it will be interpreted as the arguments coming as key-value pairs (the shortcut to send many arguments at once). That means that the function must have other arguments we need to supply, so that the struct is not the only one:
myService.addCartItem(cartItem,customerId);
Or just send a bogus second argument to fool the gateway:
myService.addCartItem(cartItem,"");
We can then access this structure from ColdFusion as:
arguments.product.product_id
The other way around should be simple, but it does have some caveats due to the case-sensitive nature of Flash. In ColdFusion there are basically two way of creating and populating an structure:
Method A:
Method B:
When these two structures are used within CF, there is no difference between them, because CF is case-insensitive. If we send that to Flash:
it will receive different structures depending on what method was used to create them. With method A, it will receive results.PRODUCT_ID or results["PRODUCT_ID"], while if method B was used, it will receive results.product_id or results["product_id"] (what we most likely intended and expect).
It’s interesting to note that if you send a component instance to Flash, it will receive only the public properties (defined by this.myProperty), but no matter how we defined them (even by this["myProperty"]), they will always be upper case.
RecordSets are handy complex objects that can carry a lot of data, which we can manipulate, sort and show in controls such as data grids. But you may be wondering what is a RecordSet beginning with. In ColdFusion terms, a RecordSet is what a query becomes when it reaches Flash. It contains the names of the columns and an array of records. It has several functions we can use such as getItemAt() or getColumnNames(). Going over all the features of a RecordSet is beyond this article.
RecordSet can only go one way, from the server to Flash. In ColdFusion, we just send a plain query.
SELECT *
FROM product
I hope this overview helps you understanding how to use Remoting in the different scenarios that you may encounter. If you have any question, fell free to post it in the comments.