Posted in ColdFusion | Posted on 08-22-2006 | 9,699 views
I've had to use AJAX (specifically Spry) now with two Model-Glue sites, so I thought I'd share how I've done it. This isn't rocket science per se, but I'm pretty happy with my method and I thought others might like to see it as well. (And the flip side is that if I'm doing something totally stupid, folks will let me know pretty quickly.)
First - let me start with the basics. Any page using Spry will need to:
- Include the Spry libraries
- Load the XML data
Let me first talk about how I included the Spry libraries. I could have simply included the libraries in my layout view. However, the Spry libraries aren't the skinniest files around, so I didn't want to do that. What I've done is simply tell the viewState when to load Spry. So if a page needs Spry, I'll do this:
My layout view then does:
2.....
3<cfif useajax>
4<script type="text/javascript" src="/js/xpath.js"></script>
5<script type="text/javascript" src="/js/SpryData.js"></script>
6</cfif>
For those who don't quite know Model-Glue, all this means is that by default, the Spry libraries will not be loaded. A view has to be explicitly state that it wants to load them.
The next thing I need to do is load the XML. Here is one sample of doing that using Spry:
Note the use of Model-Glue syntax to construct the URL. I also decided that all XML based events will be named xml.something. In this case, xml.issues. Now let's look at the event definition:
2 <broadcasts>
3 <message name="GetIssues" />
4 <message name="ToXML">
5 <argument name="viewstatekey" value="issues" />
6 <argument name="xmlpath" value="issues/issue" />
7 </message>
8 </broadcasts>
9 <views>
10 <include name="body" template="xml.view.cfm" />
11 </views>
12 <results/>
13</event-handler>
What's going on here? First I have my generic call to get my data. In this case, it is GetIssues. This is going to fetch a query of data. Now for the cool part. The ToXML message is how I convert the query to XML. I pass in the viewstatekey. This is simply telling the method where to look for data. In this case the value is issues, meaning that GetIssues set it's data in a key named issues. Next I pass in the xmlpath that should be used when creating the XML.
What all of this means is - I can use ToXML in multiple places by simply telling it where the data is and how to construct the XML. It is a generic event that can be used for multiple Spry events. The view (xml.view.cfm) simply returns the XML:
2
3<cfset xml = viewState.getValue("xml")>
4
5<cfcontent type="text/xml"><cfoutput>#xml#</cfoutput>
6
7
8<cfsetting enablecfoutputonly=false>
In case you are wondering, the code that creates the XML is simply my ToXML cfc (which will hopefully have a proper project page soon). This is how it was done in the controller:
2 <cfargument name="event" type="any">
3 <cfset var viewKey = arguments.event.getArgument("viewstatekey")>
4 <cfset var xmlpath = arguments.event.getArgument("xmlpath")>
5 <cfset var xmlroot = listFirst(xmlPath, "/")>
6 <cfset var xmlchild = listLast(xmlPath, "/")>
7 <cfset var data = "">
8
9 <cfif arguments.event.valueExists(viewkey)>
10 <cfset data = arguments.event.getValue(viewkey)>
11 <cfset xmlData = variables.xmlCFC.queryToXML(data, xmlroot, xmlchild)>
12 </cfif>
13
14 <cfset arguments.event.setValue("xml", xmldata)>
15</cffunction>
As you can see, nothing too fancy here. The main thing to note is how it was built in a generic fashion.


All that said, two caveats. First, I haven't done this so it is just my opinion/theory. And second, I'm sure it is probably possible to use a MG app as the backend of a Flex app. It just seems like trying to bend it to do something it wasn't really meant to do.
You've done more Flex work than I have Ray so maybe you can discuss this a bit further? The way I understand it, a Flex app works something like this:
Flex app announces event. Flex hits a CFC on the server (a Facade that in turn calls a Service layer component in the application scope) for some data. Data is returned via Flash Remoting. Flex populates a data object. View components in the Flex app that are bound to the data object update themselves in response (such as a data grid). Am I near the mark? Maybe one difference is that I'm talking about Flash Remoting and your blog entry is about XML formatted data for AJAX. They are kind of different beasts I believe.
Thanks.
I think you are right on your understanding of Flex (I doubt I am much more experienced in it then you), but in your text where you say, 'flex hits a service layer', I see no reason why that can't be the MG controllers.
In other words, my MG controllers are pretty "dumb", and simply delegate to the Service layer. In this setup, maybe it makes more sense why I think keeping Model-Glue "in the way" so to speak seems redundant. If the model changes, that's fine, because the Service layer interface is meant to stay consistent. In fact, with such a setup, it would be possible to have a MG app running an HTML version of the site, but a Flex app making calls directly to the Service layer though some simple Facade components.
Maybe this would clear it up a bit for me: if you used a MG app as the back-end for a Flex app, and you wanted to use Flash Remoting as the data exchange mechanism, what exactly would the Flex app be "calling" in the MG app? Wouldn't you have to create a static Facade CFC to point the Flex remoting calls to? AFAIK, you can't point a Flash Remoting call at a normal URL, it must be pointed at a CFC method whose access is "remote". What do you think? Thanks.
But anyway - you are right - I'd have a facade CFC and be skipping MG anyway. So forget what I said I guess. ;) Although I'd probably still have MG there to support a web based admin.
I'd imagine that applications with data persistence would have service objects that had CRUD methods. However, I always get a bit confused on how best to create service objects when objects are composed of each other, i.e. do I need a separate user service, company service, address service etc.
I guess this is the problem with modeling objects from the database structure, which is very tempting when using something like Reactor! Anyway, do either of you have advice on how best to model services? Or know of any indicators that help identify when persistence objects should be aggregated under one service object (if that makes any sense)?
Thanks,
Using MG Unity to get this done means that you have already wired up your "Service" cfc's in ColdSpring. ColdSpring has an object called RemoteFactoryBean. This allows you to have ColdSpring generate your remote facade for you from your main service object. So you can enjoy the benifits of Ajax/Flex without having to write extra code to interface it with the business code you have already written. Through AOP in ColdSpring you can also implement security, logging and even "massage" the data you return.
I made a blog post about it a while ago. Which you can see here http://www.simb.net/client/index.cfm/2006/7/6/Cold...
I think the Flex question also addresses the use of web services with frameworks as well. In MG as well as Mach-II, you will have to have a facade CFC that will have to be directly accessed at the controller layer. This will suite FR and webservices.
For web services, I guess you could have a view that generates the WSDL and all, but that seems like a bit of overkill when CF will do it for you.
The more I look at it, creating "Remote Facades" or "wrapper CFCs" in the controller makes the most sense. Although, doing that makes the consuming service an extension of the controller, I would think. That, however, is more of a theoretical issue than practical.
Is there any way in ColdFusion to use function pointers? For instance, in my spryListener, I want a simple function called 'proxy'.
<cffunction name="proxy" access="public" returntype="xml" output="false">
<cfargument name="event" type="MachII.framework.Event" required="yes"/>
<cfset var proxyFunction = event.getArg('method') />
<cfreturn variables.spryGateway.#proxyFunction#() />
</cffunction>
This doesn't compile, but illustrates what I want to do. I want to be able to supply any function name to the listener and have it call the appropriate function in the spryGateway (which builds the actual xml).
This would allow me to build Spry datasets like
var dsRecentUsers = new Spry.Data.XMLDataSet("index.cfm?event=spry&method=recentLogins", "users/user");
The spry event notifies the spryListener, passing the name of the function to be called in the spryGateway as the "method" parameter.
<cfinvoke component="#spryGateway#" method="#proxyFunction#" etc>
<cffunction name="proxy" access="public" returntype="xml" output="false">
<cfargument name="event" type="MachII.framework.Event" required="yes"/>
<cfset var proxyFunction = event.getArg('method') />
<cfset var myXML = '' />
<cfinvoke component="#variables.spryGateway#" method="#proxyFunction#" argumentcollection="#event.getArgs()#" returnvariable="myXML" />
<cfreturn myXML />
</cffunction>
seems to be working at first blush. The argumentcollection is picking up any additional parameters properly also, ie.,
var dsRecentUsers = new Spry.Data.XMLDataSet("index.cfm?event=spry.facade&method=recentLogins&NumberDays={dsDays::day}", "users/user", {useCache: false});
this is really a nice description. I am trying to implement some parts of a MG:U app with AJAX (probably Spry) and want to integrate your idea. The problem is the debugging information provided by MG. Everything works fine while not using it (debug=false in CS configuration). Is there a way to let MG render the view without the debug output and NOT setting it to 'false' globally in the configuration? I haven't found a way to do this yet, so I am interested if anyone else got that working.
Greetings,
Jan from Germany
<cfset request.modelGlueSuppressDebugging = true />
to the xml.view.cfm and everything works fine!
[Add Comment] [Subscribe to Comments]