Using Spry JSON Data sets with ColdFusion 8

Spry is an open source Ajax Framework developed by Adobe.  Event though Spry is IDE agnostic, it ships with Dreamweaver (since version CS3).  It is visible in Dreamweaver as a ‘Spry’ Insert bar either at the top of the screen (CS3) or on the side (CS4).

Spry Insert Bar in DreamWeaver CS4

Spry Insert Bar in DreamWeaver CS4

Recently, I decided that is was about time to really invest the necessary time for me to look seriously into Spry (current release is 1.6.1).  As I went through the first pages of the documentation and started out a few sample files on my own, I quickly discovered how easy and yet powerful Spry can be.

Encouraged by these first results, I wanted to know more about Spry, but from a ColdFusion developper prospective.

A few words about Spry

The Spry Framework is divided into 3 parts.

The first part is the Spry Data sets.   A dataSet is a javascript Object that hold data from a datasource.  The amazing thing about a Spry DataSet is its ability to load data from a very wide variety of sources.  The Data can come from an XML file, an HTML file (an HTML table, or an HTML list can be used as a datasource for a Spry dataSet), or a Json DataSource.  Once the data is loaded into the Javascript Object, it can be manipulated in a variety of ways, including, filtering, sorting and paging through the dataset.  Those manipulations can be done the same way regardless of the origin of the data (XML, HTML, Json).  Once the dataSet is created, time to rock’n roll….

The second part is Spry Widgets. A Spry Widget is a page element that combines HTML, CSS and Javascript code to enable user interaction.  Example of widgets includes Tabbed panels, menu bars, Form validation scripts, sliding panels…

The third part is Spry Effects. Spry Effects are visual enhancement that you can apply to (almost) any HTML elements.  Effects include Fade, Slide, Grow, Move, Opacity, Color,…

This post is about JSON datasets and how to dynamically use Ajax to asynchroniously load new data into the dataSet with a ColdFusion 8 back End

1) The ColdFusion Code

For this App, I have created one CFC file (called spryExapmple.cfc) with 3 functions.

The first function queries the database to retrieve the list of artists that have associated artWork.  This query is used in the Front end to populate the drop-down list using a CF Select.

<cffunction name="getArtistWithArt" access="public" returntype="query">
   <cfset var qArtists = ''/>
    <cfquery datasource="cfartgallery" name="qArtists">
      SELECT DISTINCT ARTISTS.ARTISTID, ARTISTS.LASTNAME
      FROM APP.ARTISTS INNER JOIN APP.ART ON ARTISTS.ARTISTID = ART.ARTISTID
      ORDER BY LASTNAME ASC
    </cfquery>
  <cfreturn qArtists/>
 </cffunction>

The second Function queries the database to retreive a list of artWork associated with an artist.

<cffunction name="getArtByArtist" access="remote" returntype="array" returnformat="Json">
 <cfargument name="artistId" type="numeric" required="yes">
   <cfset var qArt = ''/>
   <cfset var aArt = ''/>
   <cfquery datasource="cfartgallery" name="qArt">
     SELECT ARTID, ARTNAME, LARGEIMAGE, DESCRIPTION
     FROM APP.ART
      WHERE ARTISTID = <cfqueryparam value="#arguments.artistId#" cfsqltype="cf_sql_integer"/>
   </cfquery>
  <cfset aArt = queryToArray(qArt)/>
  <cfreturn aArt>
 </cffunction>

This second function really is where it all happens.  Notice the access attribute set to ‘Remote‘.  This will be handy in order to call this CFC function from our Javascript client application when we’ll need to refresh the data in the dataset with artWork from another artist.  Also notice the ‘returnType‘ attribute set to ‘Array’ and the ‘returnFormat’ set to ‘Json‘.

The ReturnFormat attribute is a new feature of Colfusion 8.  It is used to specify the returnFormat to a remote caller.  In other words, this attribute makes sense only on functions that have an access set to ‘remote‘.  If the function’s access attribute is not ‘Remote‘, or if the function is called from within the coldFusion App, the ‘returnFormat‘ is ignored.  But, if the function is called from a remote caller (as it will be the case with our ajax application that will remotely call this CFC function), then the returnFormat attribute will have coldFusion serialize its native data type (in this case an array as specified in the returnType attribute) in another format (in this case Json) before returning it to the remote caller.  Other values for the ‘returnFormat’ attributes are json, wddx and plain

The third function is a private method that converts the Query returned by the second function into an array of structure.  This is made to make it easier to use the data on the Ajax app, but there  certainly are other ways.

<cffunction name="queryToArray" access="private" returntype="array">
  <cfargument name="q2convert" required="yes" type="query"/>
    <cfset var aToReturn = ArrayNew(1)/>
    <cfset var thisRow = ''/>
    <cfset var stThisRow = StructNew()/>
    <cfset var columnList = q2convert.columnList/>
    <cfset var rowCount = q2convert.recordCount/>
    <cfloop from="1" to="#rowCount#" index="thisRow">
      <cfloop list="#columnList#" index="thisColumn">
         <cfset stThisRow[thisColumn] = q2convert[thisColumn][thisRow]/>
      </cfloop>
      <cfset ArrayAppend(aToReturn, Duplicate(stThisRow))/>
   </cfloop>
   <cfreturn aToReturn/>
 </cffunction>

2) The HTML page code

The example I worked with is very simple.  It is an HTML table that display some art Work data  retrieved from a database (the DB that ships with the CF developer edition).  On top of the table, there is a Drop down Select control used to choose an artist.  The idea is to use Spry to dynamically change the data when an Artist is chosen in the Drop Down list.

Final result of the test page

Final result of the test page

  • Creating the dataSet

The first thing to do is to insert the needed Javascipt files provided by the Spry Framework.

<script type="text/javascript" src="SpryAssets/SpryData.js"></script>
<script type="text/javascript" src="SpryAssets/SpryJSONDataSet.js"></script>

Then, it is time to create the Dataset.  Remember that the  ‘getArtByArtist‘ funcion defined in our CFC file can be accessed remotely and that it takes one required numeric argument : the ID of the artist.

var ds1 = new Spry.Data.JSONDataSet("components/spryExample.cfc?method=getArtByArtist&artistId=4");
ds1.setColumnType('ARTNAME','string');

Those 2 lines of code are Spry Json Dataset in action!  The first line creates a local variable called ‘ds1′ to save the dataset.  In this case, the  Spry.Data.JSONDataSet constructor function takes one argument  that is the URL of the Json data to load into the DataSet.  Notice that the url directly points to our CFC file.  Also notice how URL parameters are used to specify the method and to pass arguments.  Obviously, this can only be done with remote methods.  The value of the argument (4) has been randomly chosen (by Me!) to populate the initial dataset.

The second line is used to force Spry to cast the name of the ArtWork as a string.  In the initial testing of app, I had some problem with a piece of art called ’1958′.  Spry parsed it as a number and then could not identify the other artNames in the dataset (I had a NaN instead of the actual name).

  • Displaying the data in a table

This is the step where the Power and easiness of Spry will definitely blow you mind off.  First of all, we need to define an area of our page as a spry Region.  A spry region is an area of the page that is bound to a Spry DataSet.  It basically works as a <cfoutput> tag block as spry dynamic data can only be outputted in such region.  A spry region also has the ability to regenerate itself whenever the dataset to which it is bound changes. Typically, a <div> tag is used as a spry region, but keep in mind that almost all HTML elements can be used as a spry dynamic region (exceptions include tags  <table> and <tr> that can NOT be used as spry regions)

<div spry:region="ds1">
   <table border="1">
     <tr>
       <th>Name</th>
       <th>Description</th>
        <th>Image</th>
    </tr>
    <tr spry:repeat="ds1">
       <td>{ds1::ARTNAME}</td>
       <td>{ds1::DESCRIPTION}</td>
       <td>{ds1::LARGEIMAGE}</td>
    </tr>
   </table>
</div>

Notice the custom ‘spry:region‘ attribute and its value ‘ds1‘ set to the name of the dataset.  Also notice the ‘spry:repeat‘ attribute on the <tr> tag.  This acts as a loop to ensure that all the rows of the dataset will be outputted (exactly like <cfoutput query=”xxx”>).  Finally, notice the way the data from the dataset are bound to the table cells.  It uses the curly braces syntax that most of you probaly know from Flex and ActionScript.  {DatasetName::ColumsToPrint}.

  • Using the Spry functionality to refresh the data when an artist is selected in the dropDownList

Outputting the dropDown list is a piece of cake.  Using the Method defined in our CFC and the enhanced capabilities of <cfform> and <cfselect>, all it takes is the following 4 lines of code.

<cfset qArtists = CreateObject("component", "xmlExport.components.spryExample").getArtistWithArt()/>
<cfform id="frmArtists">
   <cfselect name="artistId" query="qArtists" value="ArtistId" display="LastName" onChange="javascript:loadArtistData(this.value);"></cfselect>
</cfform>

Notice the ‘onChange’ event in the cfSelect.  It calls a javascript function (loadArtistData)  and passes the Id of the chosen artist to the function.

This function will be responsible of asynchoneously calling the remote ‘getArtByArtist’ function defined in our server side CFC, and then, to refresh the data in the dataset when the response comes back from the ColdFusion server.

function loadArtistData(artistId)
 {
   var theUrl = "components/spryExample.cfc?method=getArtByArtist&artistId="+artistId;
   ds1.setURL(theUrl);
   ds1.loadData();
 }

And… it’s all done!  Don’t pinch you.  This really is all it takes to make it work.  In this javascript function, we are using a lot of features that the Spry framework provides to us.

  • The setUrl() method is used to change the url from which the dataset is obtaining its data.
  • the loadData function is used to call the new URL, retreive the new data (remember the ‘returnFormat= “json”‘ on the remote CFC method), and load the new data in the dataset
  • The fact that all’ spry:region‘ register themselves as an observer of their related dataset.  It means that the ‘spry:region‘ automatically updates when the underlying dataset changes.

Links

The complete app used in this post.

The Spry home page on Adobe Labs. (Download the open source Spry framework from this address)

The Spry Live Docs

The Spry API reference

The Spry Developer center (Example and tutorials can be found there)

Share
back

3 comments

  1. AJ says:

    I have tried to get this to work and for some reason I can’t view the detail area and the finale result doesn’t seem to work either.

  2. Damien says:

    Hello.
    If you are working on a server where debug output is enabled, it is possible that the script I provided fails. It is due to the fact that the method on the server adds the debug information to the content it is supposed to generate, making its response impossible to parse and use by the javascript client.
    You can turn off the debug output by adding the following line of code anywhere in the CFC method :
    <cfsettings showdebugoutput=”no”/>

    Hope it helps
    Damien

Leave a Reply

Your email address will not be published. Required fields are marked *

*