Blog

1Feb
2006
The Keys To Getting CFC ActionScript Auto-Conversions To Work

An important benefit of the new and improved Flash Remoting adapter (installed with the Flex updater for ColdFusion MX 7.01, and used in conjunction with Flex 2) is the automatic conversion of objects between ColdFusion and Flex - CFCs mapped to ActionScript classes, and vice-versa.

For example, suppose you had a User.cfc in ColdFusion and a User.as in Flex. If you returned the User ActionScript class to ColdFusion what you'd get on the ColdFusion end is an instance of User.cfc. And when a Flash Remoting call returns a User.cfc (or perhaps an array of them), what you'd end up with in Flex is a User class (or an array of them).

This conversion is automatic, but there are some very specific requirements that you must meet (miss any of them any the data sent back and forth will be a generic object):

  • Both the CFC and the ActionScript class must have the exact same properties (same name, same type, and same order). And yes, this does mean that you'll need to use the <cfproperty> tag in your CFC.
  • The full path to the CFC is needed, and this means that you must instantiate it using a fully qualified dot notation path (even if the CFC is in the current directory where just the CFC name would usually suffice).
  • The ActionScript object must be associated with the CFC using the [RemoteClass] (which expects the fully qualified path to the CFC).

The following snippets from an example application I posted demonstrate this. This first snippet is from Phone.cfc. Notice that it defines 10 properties (the code also contains over 20 methods which are not included here):

view plain print about
1<!--- Phone component --->
2<cfcomponent>
3
4    <!--- Define properties --->
5    <cfproperty name="name" type="string" default="">
6    <cfproperty name="description" type="string" default="">
7    <cfproperty name="price" type="numeric" default="0">
8    <cfproperty name="image" type="string" default="">
9    <cfproperty name="series" type="string" default="">
10    <cfproperty name="triband" type="boolean" default="false">
11    <cfproperty name="camera" type="boolean" default="false">
12    <cfproperty name="video" type="boolean" default="false">
13    <cfproperty name="highlight1" type="string" default="">
14    <cfproperty name="highlight2" type="string" default="">
15
16    <!--- Initialize vars --->
17    <cfscript>
18    THIS.name="";
19    THIS.description="";
20    THIS.price=0;
21    THIS.image="";
22    THIS.series="";
23    THIS.triband="false";
24    THIS.camera="false";
25    THIS.video="false";
26    THIS.highlight1="";
27    THIS.highlight2="";
28    
</cfscript>
29    
30...
31    
32</cfcomponent>
This next snippet is the code that instantiates Phone.cfc for returning back to Flex. The fully path to the CFC is specified (although not needed for ColdFusion itself, as the files are in the same directory):
view plain print about
1<!--- Create phone object --->
2<cfinvoke component="CFIDE.samples.Phones.CF.Phone"
3    method="populate"
4    argumentcollection="#args#"
5    returnvariable="phone">

This next snippet is the Phone class (file Phone.as). [RemoteClass] maps it to the appropriate CFC, and it contains the exact same 10 properties as Phone.cfc. (It does not have the same methods though, and has other methods that are not in the CFC):
view plain print about
1// Phone class (maps to Phone.cfc)
2package
3{
4    [Bindable]
5    [RemoteClass(alias="CFIDE.samples.Phones.CF.Phone")]
6    public class Phone
7    {
8        // Properties        
9        public var name:String;
10        public var description:String;
11        public var price:Number;
12        public var image:String;
13        public var series:String;
14        public var triband:Boolean;
15        public var camera:Boolean;
16        public var video:Boolean;
17        public var highlight1:String;
18        public var highlight2:String;
19
20        public function Phone()
21        {
22        }
23
24        // toString()
25        public function toString():String
26        {
27            return "Phone: " + name;
28        }            
29    }
30}
And with that done, automatic conversion can take place.

As you can see, with a little care and planning, automatic conversion of objects back and forth between ColdFusion and Flex 2 is very doable (not to mention incredibly useful).

Comments (38)



  • Jason Nussbaum

    So what's the trick to doing this with CF 6 and Flash MX 04/AS 2.0?

    :D

  • Ben Forta

    Jason, no can do. First of all it requires the new Flash Remoting Adapter which will only work with CFMX7.01, and it also needs new Flex bits.

    --- Ben

    #2Posted by Ben Forta | Feb 1, 2006, 03:08 PM
  • Sam Shrefler

    Ben:

    Will passing Objects as properties in an Object be possible. For example, creating a Person.cfc/Person.as where one of the properties is your Phone.cfc/Phone.as?

    <cfproperty name="myPhone" type="Phone(fully qualified I'd assume)">

    Jason:

    Just yesterday I posted a sample application that shows how to do it with CF 7 (not sure why 6 wouldnt' work) and AS 2.0. Check it out at http://www.shrefler.net/blog/

  • Jason Nussbaum

    Ben - That's what I thought. Thanks for the quick response.

    Sam - no link in your post...?

  • Jason Nussbaum

    Same - found it. :)
    One of those days...

  • Sam Shrefler

    Jason:

    <a href="http://www.shrefler.net/blog/2006/01/contactmanage...;

  • darron

    I'm very excited to see this functionality finally arrive in ColdFusion - it's about time CF remoting was as good as OpenAMF.

    #7Posted by darron | Feb 1, 2006, 03:37 PM
  • Ben Forta

    Sam, yes, a property can indeed be a fully qualified CFC.

    --- Ben

    #8Posted by Ben Forta | Feb 1, 2006, 03:49 PM
  • Shigeru

    I have one question.

    Can we use ColdFusion/Flex connectivity with ColdFusion Standard Edition without Flex Enterprise Service 2 ??

    #9Posted by Shigeru | Feb 2, 2006, 02:42 AM
  • Devin

    When trying to run your software, I get the following error:

    Error: code:Client.Error.MessageSend string:'Send failed' detail:'Channel.Connect.Failed error code:NetConnection.Call.Failed string:HTTP: Status 405 details: http://localhost/flex2gateway/'
       at mx.rpc::AbstractInvoker/http://www.macromedia.com/2005/flex/mx/internal::f...()
       at flash.events::EventDispatcher/dispatchEvent()
       at mx.messaging::MessageAgent/fault()
       at mx.messaging::Producer/fault()
       at mx.messaging::ChannelSet/mx.messaging:ChannelSet::faultPendingSends()
       at mx.messaging::ChannelSet/handleChannelFault()
       at flash.events::EventDispatcher/dispatchEvent()
       at mx.messaging::Channel/mx.messaging:Channel::connectFailed()
       at mx.messaging.channels::NetConnectionChannel/mx.messaging.channels:NetConnectionChannel::connectFailed()
       at mx.messaging.channels::AMFChannel/mx.messaging.channels:AMFChannel::statusHandler()


    Appears to have something to do with my modifications to the endpoint node in the flex-enterprise-services.xml. I'm using IIS, not the built-in webserver, so I modified it to
    <endpoint uri="http://localhost/flex2gateway/" class="flex.messaging.endpoints.AMFEndpoint"/> which looks correct to me

    #10Posted by Devin | Feb 2, 2006, 06:16 PM
  • Devin

    I'm thinking it has something to do with not being able to get the cf flex updater to install correctly. in the installation log, i get a warning and two errors
    ---------------------------------------
    Copy File: Destination:
    Status: WARNING
    Additional Notes: WARNING - Source C:\JRun4\wwwroot\WEB-INF\web.xml does not exist.
    ---------------------------------------
    Modify Text File - Single File: No Target Chosen
    Status: ERROR
    Additional Notes: ERROR - Unable to locate ASCII text file to be manipulated. Deferring...
    ---------------------------------------
    Modify Text File - Single File: No Target Chosen
    Status: ERROR
    Additional Notes: ERROR - Unable to locate ASCII text file to be manipulated. Deferring...



    Why it thinks I have a web.xml file located at, C:\JRun4\wwwroot\WEB-INF\web.xml, I have no idea!

    #11Posted by Devin | Feb 2, 2006, 06:36 PM
  • Ben Forta

    Shigeru, yes. Connecting via SOAP or AMF works with ColdFusion Standard, and FES is not needed for that.

    --- Ben

    #12Posted by Ben Forta | Feb 2, 2006, 09:23 PM
  • Shigeru

    Thank you, Ben!!

    #13Posted by Shigeru | Feb 5, 2006, 10:21 PM
  • Fred

    Hi,

    Will the remoting components for AS2 be updated to enable us to do this with CFMX 7 any time soon? It's frustrating the AMFPHP has this kind of functionality, but as CFers we can't do this!

    AS2 can recognise CFC instances passed to it via flash remoting, but all the properties seem to be in uppercase, and I've had no luck passing an instantiated class back from AS2 to CF.

    :(

    #14Posted by Fred | Feb 17, 2006, 11:55 AM
  • Shigeru

    Hi.

    I have a question again.
    About "flex2gateway" inside "flex-enterprise-services.xml".

    If we write like <endpoint uri="http://localhost/flex2gateway/";
    and we compile "cfdatatypes" for example.
    The swf connect to localhost's flex2gateway
    and the swf can connect from localhost but can't connect from other machine.

    If we write like <endpoint uri="http://foo.com/flex2gateway/";
    and we compile "cfdatatypes" for example.
    The swf connect from any machine if machines access to "http://foo.com/CFIDE/samples/cfdatatypes/cfdatatyp...;.
    But if we copy this swf to "http://hoge.com/CFIDE/samples/cfdatatypes/cfdataty...;, swf can't access to "http://hoge.com/flex2gateway/";.
    This must be a security reason.

    If we distribute a package software including CF/Flex Connectivity feature, Do we have to compile it for each a sever environment??

    #15Posted by Shigeru | Feb 19, 2006, 12:35 PM
  • Shigeru

    Hi.

    This is a self response.

    The answer was like below, right?
    <endpoint uri="http://{server.name}:{server.port}{context.root}/flex2gateway/"

    I am sorry to ask a stupid question again.

    #16Posted by Shigeru | Feb 22, 2006, 06:57 AM
  • Mike Rankin

    This is probably a silly question, but how do you get an object that is returned from cf back into the .as object?

    Here is the scenario I'm working with:

    I create a user object in flex and set values to its username and password properties. I then pass it to cf using a remoteObject. Cf seems to work with it fine and makes the translation from the as object to cf object. I then look up the user from the database using the supplied properties. If found, I populate a few more simple properties and return the modified object back to flex.

    In the result handler, the ResultEvent has all of the populated properties available, but it looks like it is a generic object. What I would like to do is have the returned object replace/update the properties of the original user object created in Flex that still has only the username and password populated. I've been unable to simply assign the returned object to the original because of type coercion problems.

    I can set the properties in the .as object using mutators, but that seems like one of the things we're trying to avoid with the auto-conversion.

    Am I missing some simple concept that will make this work?

  • Mike Rankin

    Hi Ben,

    I was playing around with your example to explore returning a single object instance, and the only way I was able to get the Phone component to come back from CF and auto-convert was to change everything to lowercase.

    so CFIDE.samples.Phones.CF.Phone became cfide.samples.phones.cf.phone

    Once I did that, a single instance of the phone object would work as expected.

    Is there some installation issue that might be causing CF to return the object types as all lower-case? Is there something that can be done to translate the two like we do with the cfproperty tags?

  • barry.b

    In the phone CFC:

    there's a lot of "THIS" scope being used where people usually use variables scope....

    the mapping between CFC and AS class is done via CFPROPERTY only, yes? (and nothing to do with using the "THIS" scope)?

    thanx

    #19Posted by barry.b | Mar 20, 2006, 06:16 PM
  • Ben Forta

    Barry, yes, cfproperty tags only.

    --- Ben

    #20Posted by Ben Forta | Mar 20, 2006, 07:25 PM
  • Mike Rankin

    I just tried putting a property into the variables scope and I can't seem to get it to come back into flex. Does anybody have an example of this actually working?

    Earlier I posted a comment here about problems with the returned object populating the values in the .as object. It took me forever to find, but I eventually figured out how to work around the issue. What I found was that CF was returning the object type all in lower-case, so it wouldn't match the expected case. Flash then treats it like a generic object and the conversion fails. When I went through the application and turned all of the directories and types to all lower-case, it worked fine.

    I'm not sure if that is the intended behavior or if I have something coded strangely, but it does seem to work.

  • darron

    It's a known issue that the CF/Flex connector doesn't preserve case correctly. The workaround for the time being is to use mutliple RemoteClass meta-data.

    [RemoteClass(alias="com.whatever.MyClass")]
    [RemoteClass(alias="com.whatever.myclass")]
    public class MyClass { ... }

    The first RemoteClass will convert AS -> CFC correctly, and then the second Remoteclass will convert the lowercase typed object from CFC to the proper AS3 class.

    This should be fixed in the next update to the connector.

    #22Posted by darron | Mar 20, 2006, 09:54 PM
  • Kenny Silanskas

    I have an AS class that has a property typed as ArrayCollection. Since CF does not support this property type, how can I ensure that the CFC maps correctly?

    Will Array work?

    #23Posted by Kenny Silanskas | Sep 23, 2006, 11:14 AM
  • Ben Forta

    Yep, it'll map to an array in CF.

    --- Ben

    #24Posted by Ben Forta | Sep 25, 2006, 12:57 PM
  • Jeff Houser

    i know this an old post, but I just spent some time on this today. the post was very helpful in getting this working.

    I didn't see this in the post or other comments, but since i stumbled on it I thought I'd specify for future readers.

    You need to be very careful of case sensitivity in your path names. ColdFusion doesn't care. IIS doesn't care. ActionScript cares.

  • Russ

    Anyone have any idea why when testing this over a network, meaning my CF is on a seperate computer from my workstation, and using the fully qualified dot notation path to the cfc, I get an error saying it cannot find the cfc?

    #26Posted by Russ | Apr 17, 2007, 07:30 PM
  • Jeffry Houser

    There could be plenty of reasons.

    If I understand, you are compiling your swf on your local machine and expect it to call CFCs, via RemoteObject / Flex Remoting, on a remote server. Is that correct?

    You'll need a crossdomain.xml on your CF server that will allow 'localhost' to access it. That might be one problem.

    You will need a local services-config file that will point to the flex remoting instance on your CF server.

    I'd check those two things first.

  • Pat

    Does anyone have an example of using this with typed arrays as a property type? For example, passing back an Order object with an order_items property that contains a typed array (project.myCFCS.order_item[])?

    Everything works until I add it, then I get an ObjectProxy returned. I've tried returning just an array as well to no avail. I'll provide source outside this blog if requested.

    Thanks!
    -Pat
    www.datanotion.com

    #28Posted by Pat | Jul 26, 2007, 02:03 PM
  • Jeffry Houser

    If I understand correctly, you are trying to return an object which contains an array of objects. Is that correct? And the array of objects is seen as a generic object, not as your custom object. Is that correct?

    First, make sure that the order_Item object will pass back and forth between Flex and ColdFusion (without being put in an array). If that works, it's good; if not figure out why.

    Second, is there any situation where you create an Order_Item object as not part of the array? If you never create an instance of it, Flex won't recognize returned objects as your custom type, instead treating them as generic objects.

    I believe this is because the Flex compiler does not add uninstantiated classes to the compiled swf. You need to create that instance of a class, not just import it.

    If important, I do this empty instantiation in the Cairngorm model.

  • Shawn

    This question was asked a couple of months ago but never answered...
    Why is there a problem with Flex accessing CFMX components that have
    data stored in the variables scope ? I'm calling a function that queries
    from a query that is stored in the variables scope. Calling the component
    from a CFMX template page works fine, but when calling the component from Flex,
    the component says that the variables-scoped variable does not exist.

    #30Posted by Shawn | Aug 2, 2007, 07:56 AM
  • Jeffry Houser

    Every call to a component via Flash Remoting is done in isolation of other calls.

    So, if you make a call, create a variable in the variables scope, then access that variable you should have no problem.

    However, if you make a call, create a variable in the variables scope.

    Then make another call, that variable you created will not yet be created.

    You can get around this by storing the QOQ in the application scope.

    Or even better, return the initial Query to Flex, dump it in an array collection and use the filtering functionality on the client side, without new calls to the server.

  • Shawn

    Our cfc components themselves are stored in the application scope and the "variables-scoped" variables are initialized within the component upon application startup, so in our CFMX pages we call the component, i.e. result = application.services.getUserName(id=1) and the getUserName function queries a query stored in that components variables scope. But from what you are saying it sounds like if the component gets called from Flex then the component knows nothing about its own variables-scoped variables....weird. Would cfproperty tags in the component solve this problem instead of having to push each individual variable in all of the components into the application scope ?

    #32Posted by Shawn | Aug 2, 2007, 10:12 AM
  • Jeffry Houser

    I don't think CF property is related.

    If the component is stored in the application scope, you should be able to access it w/o problems.

    Keep in mind, that

    Application.foo = createObject('component',"bar");

    creates an instance of bar, named foo in the application scope.

    But, if you are hitting that bar component from Flex, like this:

    <mx:RemoteObject destinaion="coldfusion" source="bar" />

    A new instance of bar is created, you are not hitting the one in the application scope.

    However, if you can create a component that accesses the component in the Application scope, something like this:

    <cfcomponent name="weed">
    <cffunction name="callFoo">
    <cfreturn application.foo.mymethod()>
    </cffunction>
    </cfcomponent>

    and access it from Flex like this:

    <mx:RemoteObject destinaion="coldfusion" source="weed" />

    I believe the weed component in this example is an implementation of the facade pattern.

  • Shawn

    We must have been reading each other's mind... I was thinking the same thing about making a wrapper to call the application scoped functions, and although it does require making new functions to call the existing ones, at least the core functionality of the existing ones doesn't have to be re-written. I've tested it and it works fine.

    THANKS !!!

    #34Posted by Shawn | Aug 2, 2007, 10:49 AM
  • Jeffry Houser

    Glad to help!

  • Pat

    For those that are interested I finally figured out what was causing my objects not to map. I had some IMPORT statements in the package area (right under my RemoteClass statement) and once I moved them inside the public class area, everything started mapping properly. The issue was that one of those imports was causing a new public var called "object" to appear in the .AS object, hence making it not match the exact order of the CFC object. Don't understand why or where it came from, but hey it works now.

    1 issue down.....several to go. Thanks again to the excellent Flex community!

    Thanks!
    -Pat
    www.datanotion.com

    #36Posted by Pat | Aug 3, 2007, 12:30 PM
  • dan fredericks

    This question is along the lines of all these.

    I use the flash forms in cold fusion 7, and do a bunch of .cfc calls using flash remoting. I have test servers and production servers. So far I have called the flashservies using a static url address:
    http://someaddress/flashservices/gateway/

    I need to make this address dynamic so no matter what server I put the code on, it knows the proper path to flash services. In flash, you can pass the server name from the cold fusion page the cfobject call is on.

    can anyone help me, or can this address only be static?

    thanks
    dan

    #37Posted by dan fredericks | Aug 31, 2007, 09:30 AM
  • Morgan Gustafsson

    I think there’s a fourth condition that has to be met. At least there was for me. I didn’t use cfinvoke, as in the example, instead I used:

    obj = createObject("component", "CFIDE.samples.Phones.CF.Phone").init();

    Everything worked just fine except for the automatic conversion. I checked and double-checked the properties, the path, the [RemoteClass] but nothing. The trick was to add the name attribute to the component tag as below.

    <cfcomponent name="phoneGateway" output="false">

    I'm a total newbie so this might be obvious to anyone only vaguely familiar with CF. Still I hope this might help someone like me.

    #38Posted by Morgan Gustafsson | Oct 12, 2007, 04:10 AM