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:

<cfset viewState.setValue("useajax", true)>

My layout view then does:

<cfset useajax = viewState.getValue("useajax", false)> ..... <cfif useajax> <script type="text/javascript" src="/js/xpath.js"></script> <script type="text/javascript" src="/js/SpryData.js"></script> </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:

var dsIssues = new Spry.Data.XMLDataSet("#viewState.getValue("myself")#xml.issues", "issues/issue");

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:

<event-handler name="xml.issues"> <broadcasts> <message name="GetIssues" /> <message name="ToXML"> <argument name="viewstatekey" value="issues" /> <argument name="xmlpath" value="issues/issue" /> </message> </broadcasts> <views> <include name="body" template="xml.view.cfm" /> </views> <results/> </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:

<cfsetting enablecfoutputonly=true>

<cfset xml = viewState.getValue("xml")>

<cfcontent type="text/xml"><cfoutput>#xml#</cfoutput>

<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:

<cffunction name="toXML" access="public" returnType="void" output="true"> <cfargument name="event" type="any"> <cfset var viewKey = arguments.event.getArgument("viewstatekey")> <cfset var xmlpath = arguments.event.getArgument("xmlpath")> <cfset var xmlroot = listFirst(xmlPath, "/")> <cfset var xmlchild = listLast(xmlPath, "/")> <cfset var data = "">

<cfif arguments.event.valueExists(viewkey)> <cfset data = arguments.event.getValue(viewkey)> <cfset xmlData = variables.xmlCFC.queryToXML(data, xmlroot, xmlchild)> </cfif>

<cfset arguments.event.setValue("xml", xmldata)> </cffunction>

As you can see, nothing too fancy here. The main thing to note is how it was built in a generic fashion.