Spry :: Dynamic Graphing With ColdFusion and Spry
Andrew Powell
First of all, I needed to generate the chart and XML. ColdFusion does both very well. The file is broken up into functions to increase readability. You can do all this inline, but having functional units to look at greatly increases comprehension.
genXML.cfm:
<cfscript>
function main()
{
var fullFile = expandPath("/") & "spryGraph.png";
remFile(fileName=fullFile);
if(isDefined("url.update"))
{
addPlayer(playerName=url.name,playerPosition=url.position);
}
createChartFile(chartSource=getPlayerList(),fileName=fullFile);
return createXMLString();
}
</cfscript>
<cffunction name="addPlayer" access="public" returntype="boolean" hint="Adds a player to the database">
<cfargument name="playerName" type="string" required="true"/>
<cfargument name="playerPosition" type="string" required="true"/>
<cfset var retBool = true/>
<cftry>
<cfquery datasource="#application.datasource#">
INSERT INTO players(name,position) values(<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.playerName#">,<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.playerPosition#">)
</cfquery>
<cfcatch type="any">
<cfset retBool = false/>
</cfcatch>
</cftry>
<cfreturn retBool/>
</cffunction>
<cffunction name="remFile" access="public" returntype="boolean" hint="deletes a given file">
<cfargument name="fileName" type="string" required="true">
<cfset var retBool = true/>
<cftry>
<cfif fileExists(arguments.fileName)>
<cffile action="delete" file="#arguments.fileName#"/>
</cfif>
<cfcatch type="any">
<cfset retBool = false/>
</cfcatch>
</cftry>
<cfreturn retBool/>
</cffunction>
<cffunction name="getPlayerList" access="public" returntype="query" hint="returns a list of players in query object">
<cfset var retQry = queryNew("")/>
<cfquery datasource="#application.datasource#" name="retQry">
SELECT position, count(position) as cnt FROM players GROUP BY position
</cfquery>
<cfreturn retQry/>
</cffunction>
<cffunction name="createChartFile" access="public" returntype="boolean">
<cfargument name="chartSource" type="query" required="true"/>
<cfargument name="fileName" type="string" required="true"/>
<cfset var playerList = arguments.chartSource/>
<cfset var spryGraph = ""/>
<cfset var retBool = true/>
<cftry>
<cfchart format="png" name="spryGraph" chartheight="270" chartwidth="480" show3d="true">
<cfchartseries query="playerList" itemcolumn="position" valuecolumn="cnt" type="bar">
</cfchart>
<cffile action="write" file="#arguments.fileName#" output="#spryGraph#" charset="ISO-8859-1">
<cfcatch type="any">
<cfset retBool = false>
</cfcatch>
</cftry>
<cfreturn retBool/>
</cffunction>
<cffunction name="createXMLString" access="public" returntype="string">
<cfset var retStr = "">
<cfsavecontent variable="retStr">
<cfoutput>
<fileInfo>
<fileData>
<fileName>Player File</fileName>
<filePath>spryGraph.png?cb=#round(rand("SHA1PRNG")*second(now()))#</filePath>
</fileData>
</fileInfo>
</cfoutput>
</cfsavecontent>
<cfreturn retStr>
</cffunction>
<cfcontent type="text/xml">
<cfoutput>#main()#</cfoutput>
</cfcontent>
Notice the use of CFSAVECONTENT in lieu of CFXML. This keeps your XML as a string and not a CFXML object. CFXML objects take up more memory than simple strings. In an enterprise application, this can become costly. Also notice that in the XML, a cacheBuster (cb) is being added to the file name. This is to keep the browser from reading the image out of cache.
Once the XML is generated, we need to find a way to display it. This is not too much different from the Yahoo! Traffic API example.
index.cfm:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xmlns:spry="http://ns.adobe.com/spry">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Spry Example 5 - Dynamic Data With Visualization</title>
<!-- Begin Spry Includes -->
<script type="text/javascript" src="/assets/js/spry/xpath.js"></script>
<script type="text/javascript" src="/assets/js/spry/SpryData.js"></script>
<script type="text/javascript" src="/assets/js/spry/SpryXML.js"></script>
<!-- End Spry Includes -->
<script type="text/javascript">
var dsPlayers = new Spry.Data.XMLDataSet("genXML.cfm", "/fileInfo/fileData", { useCache: false});
function updatePlayerData(playerForm,spryGraph)
{
var playerName = playerForm.playerName.value;
var playerPosition = playerForm.playerPosition.value;
var d = new Date();
dsPlayers.setURL("genXML.cfm?update=1\&position=" + escape(playerPosition) + "\&name=" + escape(playerName));
dsPlayers.loadData();
}
</script>
</head>
<body>
<form id="playerForm" name="playerForm">
Player Name: <input type="text" id="playerName" name="playerName" /><br/>
Position:
<select name="playerPosition" id="playerPosition">
<option value="Goalkeeper">Goalkeeper</option>
<option value="Center Back">Center Back</option>
<option value="Left Back">Left Back</option>
<option value="Right Back">Right Back</option>
<option value="Midfield">Midfield</option>
<option value="Forward">Forward</option>
<option value="Striker">Striker</option>
</select><br/>
<input type="button" id="playerButton" value="Add Player" name="playerButton" onclick="updatePlayerData(playerForm,spryGraph)"/>
</form>
<div spry:region="dsPlayers">
<div spry:state="loading"><img src="/assets/images/ajax-loader.gif"/></div>
<div spry:state="error">An error occured loading your dataset.</div>
<div spry:state="ready">
<script type="text/javascript">
</script>
<img id="spryGraph" src="{dsPlayers::filePath}" id="" alt="{dsPlayers::fileName}"/>
</div>
</div>
</body>
</html>
You can see a working example here.
Posted in ColdFusion | Spry | AJAX |
3 comments
Jan 9, 2008 at 12:00 AM <p>Your demo link isn't working.</p>
Jan 9, 2008 at 12:00 AM <p>Demo link fixed, but now the code layout is hosed. Go figure.</p>
Mar 4, 2008 at 12:00 AM <p>i agree does not work and contact to him goes unanswered.</p>