Mark asks a question about working with CFCs over AjaxProxy:
So this is a rather interesting situation. Most of the time I think people would use cfajaxproxy to talk to a service, not to a bean object. As Mark noticed, all the hits to the remote object are stateless. You could possibly store information in the session scope, but that would get a bit messy. So he was left with passing an ID every time.I have a ColdFusion object (CFC) designed in an OO fashion with getters and setters. I want to use the new ColdFusion 8 ajax proxy stuff to create an instance of the object and use it in javascript like I do in ColdFusion. For instance, I want to instantiate it, set a parameter, call the load() method, then be able to use any of the getter methods that I've marked as remote. I've tried doing this, but from what I can tell, you can only use this js-cf bridge as one-time things (e.g. stateless?), with the object not "remembering" the other things you've done to it. Here is a example of what I want to do that I cannot figure out how:
Then in javascript: mycfc.setId(1);
mycfc.load();
alert(mycfc.getFirstName() + ' ' + mycfc.getLastName());Anyway to do this? I can do it now calling one method at a time and passing in the id to that method, but that requires writing remote-specific methods that take the id for every call, look it up, and return that one piece of data (or stringing together multiple pieces) in the callback handler--but that is what I'm trying to avoid.
You may ask - why did he create a proxy to the bean in the first place? Why not make a proxy to a service and return the bean CFC? Well CFCs are not something you can return via ajaxproxy. To be clear, you can return a CFC no problem. But on the server side, ColdFusion will return it as a struct and return the This scope. So if you CFC had this.name="Paris", you would end up with this in JSON: { "NAME":"Paris" }. Any methods your CFC may have had will not get returned.
I can think of a few things he could do. First off - instead of returning a bean, simply return a structure. I've written beans before where all the data was stored in variables.instance. This let me then have a getAll method that would simply return variables.instance.
He could then build into the service layer a getFoo(ID) type service where the CFC would instantiate the bean, but instead of returning it, would return bean.getAll. Back in JavaScript, you would treat the result not like a bean, but a simple structure.
Another alternative - and I'm not sure I'd recommend this - is to use the This scope. As I said, my beans normally write to variables.instance.X, where X is the property. If your bean instead wrote to the This scope, like this bean:
<cfcomponent>
<cffunction name="setname" returnType="void" access="public" output="false">
<cfargument name="name" type="string" required="true">
<cfset this.name = arguments.name>
</cffunction>
<cffunction name="getname" returnType="string" access="public" output="false">
<cfreturn this.name>
</cffunction>
</cfcomponent>
You can actually return this over the wire and automatically have a structure with the right data. You still wouldn't use getName() in JavaScript, but would instead just use the NAME property.
Archived Comments
I'm currently doing something like this on the project I'm working on.
I basically do it like this:
Ajaxproxy CFC (in webroot) -> RemoteFacade.cfc (maintained by Coldspring) -> Service layer.
The frontend Ajaxproxy is a very simple CFC that takes params and then calls a method in the RemoteFacade.
The RemoteFacade takes these params and then makes calls to the service layer and returns anything that needs returning.
I have found this quite useful as the intermediate layer (RemoteFacade) can filter params so you aren't just sending the entire memento back to the client.
E.G I have a userLogin() method in Ajaxproxy and RemoteFacade. The Ajaxproxy method just passes over username and password, but then the RemoteFacade calls the login function in the UserService to retrieve a fully populated user bean which is automatically persisted in Session.
What I do now - in RemoteFacade - is to create a simple struct and only set the data I need. E.G:
<cfset returnData.isLoggedIn = user.isLoggedIn()>
<cfset returnData.error = user.getError()>
<cfset returnData.name = user.getName()>
I like this as it's filtering the data that is sent back as many of the fields in the user bean are not needed by the client.
Using a remote facade is nice as I can inject all of the dependencies using Coldspring which leaves the frontend Ajaxproxy.cfc nice and lightweight.
This should make refactoring much easier down the line..
Ray:
Thanks for blogging about this! I think your first solution (and the ideas James submitted) would work, but I still am a bit frustrated by it.
After thinking through this a bit more, I thought of an analogy of what I want to do. I like how Flex and ColdFusion can pass CFC objects "back-and-forth" where the CFC object maps to an AS class. It would be oh-so-cool to have a CF tag (and/or wizard) that creates a JS class from a CFC that would enable you to do this with JS/CF. In fact it would be cool to be able to instantiate the JS object and chain the requests a la reactor and jquery: myJSObj = new JSObj then myJSObj.init().setid(1).load().getSomeAttr(). This would give you access to more than just the attributes of a bean, but also to other functions that bean may have.
But alas, since that functionality doesn't exist (should it?), I'll look into either returning a structure with the data I need or creating some type of intermediate ajax service layer.