Adobe ColdFusion 8

Using Spry with ColdFusion

ColdFusion provides support for mixing native ColdFusion elements and Spry elements in a single application.

  • ColdFusion tags can use Spry data sets directly in bind expressions. Therefore, a ColdFusion form element, such as cfinput, can bind to a field in a dynamic Spry data set, and is updated each time the data set updates, including when the user selects an item in a Spry control or dynamic region that the data set populates.

    To bind to a Spry data set, specify the data set name followed by the path to the specific element that you bind to, by using standard Spry path syntax. For example, if dsFilters is a Spry data set with a name column, the {dsFilters.name} bind parameter binds to the value of the current row's name column. The bind parameter cannot specify an event; the bind expression is re-evaluated each time the selected row in the data set changes. The following example shows the bind syntax:

    <cfinput name="Input1" type="text"
    bind="CfC:DataManager.getInData(filter={dsFilters.name})
    

  • Spry data sets can use a CFC function as the data source. To do this, you simply specify the URL of the CFC in the Spry.Data.XMLDataSet function, just as you would invoke any remote CFC method using a URL. Specify the method name with a method URL parameter, and pass data to the function in additional URL parameters, as in the following example:
    Spry.Data.XMLDataSet("MyAppMgr.cfc?method=getFilter&filter="scores",
    "filters/filter");
    

  • The cfsprydataset tag can dynamically create and update Spry XML or JSON data sets based on ColdFusion form data. Spry dynamic regions and other elements can then use this data to control their display.

    The following example shows a cfsprydataset tag that creates a Spry XML data set named dsProducts by calling the getData.getProductDetails function and passing it the value of the selected name in a cfgrid control. The data set updates each time the name value changes.

    <cfsprydataset 
        name="dsProducts"
        type="xml"
        bind="CFC:getData.getProductDetails(prodname={myform:mygrid.name})" 
        xpath="products/product" 
        options="{method: 'POST'}"
    onBindError="errorHandler">
    

ColdFusion includes the complete Spry 1.5 framework release in web_root/CFIDE/scripts/ajax/spry directory. For more information, see the Adobe Labs Spry framework pages. For more information, see the cfsprydataset tag in the CFML Reference.

Spry data set example

This example has the following behavior:

  1. It uses a CFC function directly to populate a Spry XML data set, from an XML file.
  2. It displays information from the Spry data in a Spry dynamic region list box.
  3. It uses the selected item in the Spry data set to control the contents of a cfgrid control. The cfgrid bind expression calls a CFC and passes it a parameter bound to the selected item in the Spry XML data set.
  4. It creates a second Spry XML data set by using a cfsprydataset tag that binds to the selected item in the cfgrid control and calls a CFC function.
  5. It displays information from the second Spry data set in a second Spry dynamic region.

The example lets a user select the genre of books to display: all books, fiction, or nonfiction from a Spry list box populated from the XML file. The selected genre determines the information displayed by a cfgrid control, and a text input control shows the selected genre. The selected item in the cfgrid control determines the information that is displayed in a second Spry dynamic region.

The application consists of the following files:

  • A roundtrip.cfm page with the display controls and related logic
  • A GridDataManager.cfc file with two functions:
    • A getFilter function that gets the XML for the spry data set
    • A getData function that gets the contents of the cfgrid control
    • A getProduct function that gets detailed information on the selected book
  • A Filters.xml file with the XML data for the spry data set

For this example to display images, you must also create an images subdirectory of your application directory that contains images with the names specified by the BOOKIMAGE column of the cfbookclub database BOOKS table.

The roundtrip.cfm page

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:spry="http://ns.adobe.com/spry">

<head>
<!--- The screen.css style sheet is provided in the Spry distribution. --->
<link href="screen.css" rel="stylesheet" type="text/css" media="all"/>
<!--- Include the XPath and Spry JavaScript files. --->
<script type="text/javascript" 
    src="/CFIDE/scripts/ajax/spry/includes/xpath.js"></script>
<script type="text/javascript" 
    src="/CFIDE/scripts/ajax/spry/includes/SpryData.js"></script>

<!--- Create the dsFilters Spry XML data set used to populate the FiltersList dynamic region
    that lists the filters. Call the GridDataManager CFC getFilter method directly from a
    Spry XMLDataSet function because no binding is needed. --->
<script>
    var dsFilters = new
        Spry.Data.XMLDataSet("GridDataManager.cfc?method=getFilter", "filters/filter");
</script>

<!--- Use a cfsprydataset tag with binding to generate a dsProduct Spry data set with details
    about the book grid selection. --->
<cfsprydataset 
    name="dsProduct" 
    type="xml"
    bind="CFC:GridDataManager.getProductDetails(prodname={bookform:bookgrid.TITLE})" 
    xpath="products/product" 
    options="{method: 'POST'}"
    onBindError="errorHandler">

<!--- Function to handle bind errors. --->
<script language="javascript">
    errorHandler = function(code,msg){
        alert("Error w/bind occurred. See details below:\n\n" + "Error Code: "
            + code + "\n" + "Error Message: " + msg);
    }
</script>

<!--- Specify the size of the FiltersList Spry dynamic region.
            By default it would be unnecessarily large. --->
<style type="text/css">
<!--
#FiltersList {
    height:100px;
    width: 150px;
}
-->
</style>
</head>

<body>
<!--- A Spry dynamic region containing repeated ListBoxItem controls.
    Each item specifies a filter to use in filling the book list grid. 
    The items are populated by the data from the CFC getFilter method. --->
<div id="FiltersList" spry:region="dsFilters" class="SpryHiddenRegion">
    <div spry:repeat="dsFilters" class="ListBoxItemGroup">
        <div class="ListBoxItem"
            onclick="dsFilters.setCurrentRow('{dsFilters::ds_RowID}');"
            spry:selectgroup="feedsList" spry:select="SelectedListBoxItem"
            spry:hover="ListBoxItemHover">
                {dsFilters::description}
        </div>
    </div>
</div>

<!--- A ColdFusion form with the book list data grid. --->
<cfform name="bookform">
<!--- Create a book list grid. 
        Users select the book for which to get details from this grid.
        Populate it with the results of the CFC getData method.
        Pass the method the value of the name field of the selected 
        item in the dsfilters Spry dynamic region. --->
    <cfgrid name="bookgrid"
            format="html"
            bind="CfC:GridDataManager.getData(page={cfgridpage},
                pageSize={cfgridpagesize},sortCol={cfgridsortcolumn},
                sortDir={cfgridsortdirection},filter={dsFilters.name})"
            selectMode="browse"
            width=400
            delete="true"
            pageSize=7>
        <cfgridcolumn name="TITLE" header="Name" width=200>
        <cfgridcolumn name="GENRE" header="Type" width=200>
    </cfgrid><br />
    <!--- Show the value of the name field of the selected item in the Spry dynamic region.
    --->
    <cfinput name="filter" bind="{dsFilters.name}">
</cfform>

<hr>

<!--- A Spry dynamic region that uses the dsProduct data set to display information on the
    selected product. --->
<div id="RSSResultsList" spry:detailregion="dsProduct" class="SpryHiddenRegion">
    <strong>{name}</strong><br>
    <img src="images/{bookimage}" alt="product box shot" width="238" height="130"/>
    <div>{desc}</div>
</div>

<hr>

</body>
</html>

The gridDataManager.cfc file

<cfcomponent name="GridDataManager">

    <!--- The getFilter function gets the filter XML to populate the dsFilters Spry data set.
        It specifies returnFormat=plain to send XML text. --->
    <cffunction name="getFilter" access="remote" output="false" returnFormat="plain">
        <cffile action="read" file="#ExpandPath('.')#\Filters.xml" variable="filtersxml">
        <cfcontent type="text/xml" reset="yes">
        <cfreturn filtersxml>
    </cffunction>

    <!--- The getData function returns books that match the specified genre, or all books if
        there is no genre. --->
    <cffunction name="getData" access="remote" output="false">
        <cfargument name="page" required="yes">
         <cfargument name="pageSize" required="yes">
        <cfargument name="sortCol" required="yes">
         <cfargument name="sortDir" required="yes">
        <cfargument name="filter" required="no">
        <cfquery name="books" datasource="cfbookclub"> 
            select TITLE, GENRE from BOOKS 
            <cfif isDefined("arguments.filter") AND arguments.filter NEQ "">
                where GENRE = '#arguments.filter#'
            </cfif>
            <cfif arguments.sortCol NEQ "" AND arguments.sortDir NEQ "">
                 order by #arguments.sortCol# #arguments.sortDir#
            <cfelse>
                order by TITLE ASC
            </cfif>
        </cfquery>
        <!--- Return the data only for the current page. --->
        <cfreturn QueryConvertForGrid(books, arguments.page,
            arguments.pageSize)> 
    </cffunction>
    
    <!--- The getProductDetails gets data for a single book and converts it to XML for use
        in the dsProduct Spry data set. --->
    <cffunction name="getProductDetails" access="remote" output="false">
        <cfargument name="prodname" default="The Road">
        <!--- Get the information about the book from the database. --->
        <cfquery name="bookDetails" datasource="cfbookclub">
            select TITLE, GENRE, BOOKIMAGE, BOOKDESCRIPTION from BOOKS 
                where TITLE = '#arguments.prodname#'
        </cfquery>
        <!--- Convert the query results to XML. --->
        <cfoutput>
            <cfxml variable="BookDetailsXML" >
                <?xml version="1.0" encoding="iso-8859-1"?>
                <products>
                    <product>
                        <name>#BookDetails.TITLE#</name>
                        <category>#BookDetails.GENRE#</category>
                        <bookimage>#BookDetails.BOOKIMAGE#</bookimage>
                        <desc>#BookDetails.BOOKDESCRIPTION#</desc>
                    </product>
                </products>        
            </cfxml>
        </cfoutput>
        <!--- Convert the XML object to an XML string. --->
        <cfset xmldata = xmlparse(BookDetailsXML)>
        <cfcontent type="text/xml" reset="yes">
        <cfreturn xmldata>
    </cffunction>

</cfcomponent>

The Filters.xml file

<?xml version="1.0" encoding="iso-8859-1"?>
<filters>

    <filter>
        <filterid>1</filterid>
        <name></name>
        <description>No Filter</description>
    </filter>

    <filter>
        <filterid>2</filterid>
        <name>Fiction</name>
        <description>Look for Fiction</description>
    </filter>

    <filter>
        <filterid>3</filterid>
        <name>Non-fiction</name>
        <description>Look for Nonfiction</description>
    </filter>
    
</filters>