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.