If you read the release notes for the new charting support in ColdFusion 10, you may come across this little gem:
The following server-side charting features are not available with client-side charting:
Linking charts to URL
Writing charts to a variable
While technically correct, it is possible to add support for this with a little bit of code. Here's a demonstration of one way you can add this support back in.
First, let's create a simple static chart. My technique will work for dynamic charts as well, but for the time being, let's keep things simple:
<cfchart id="main" chartheight="500" chartwidth="500" title="Test Chart" format="html" scalefrom="50" scaleto="150" >
<cfchartseries type="bar" serieslabel="Sales">
<cfchartdata item="Apples" value="120">
<cfchartdata item="Bananas" value="84">
<cfchartdata item="Cherries" value="72">
<cfchartdata item="Donuts" value="109">
</cfchartseries>
</cfchart>
To add our click handler, we're going to begin by adding ajaxOnLoad to the script. This is a ColdFusion function that handles the process of firing an event handler when client-side "stuff" is done. Yes, I know "stuff" is a bit vague. If you use jQuery, then you are familiar with $(document).ready. Since ColdFusion does a lot of front-end stuff for you when using client-side charting (and other things like cfgrid for example), you need a way to say, "Fire my JavaScript function when you're done, buddy." The ajaxOnLoad function is how that's done. Here's an updated template showing this in action:
<html>
<head>
<script>
function init() {
}
</script>
</head>
<body>
<cfchart id="main" chartheight="500" chartwidth="500" title="Test Chart" format="html" scalefrom="50" scaleto="150" >
<cfchartseries type="bar" serieslabel="Sales">
<cfchartdata item="Apples" value="120">
<cfchartdata item="Bananas" value="84">
<cfchartdata item="Cherries" value="72">
<cfchartdata item="Donuts" value="109">
</cfchartseries>
</cfchart>
<div id="data"></div>
</body>
</html>
<cfset ajaxOnLoad("init")>
So far so good. To work with the chart, ColdFusion 10 provides us with an API to get a handle on the actual object. I began by creating that handle and storing it in a variable.
}
var handle;
function init() {
handle = ColdFusion.Chart.getChartHandle();
Now we need to add the click handler. The ColdFusion 10 Beta docs show an example of the click handler, but it isn't helpful for us. The click handler tells us where the user clicked, but isn't very specific. Luckily there is a better event we can use - node_click. (For a full list of events, see this doc.)
The node_click event will fire only when a person actually clicks on one of your chart items. I began by simply dumping out the value of the event so I could see what was in it:
handle.node_click = function(dataOb) {
console.dir(dataOb);
}
This is the result:
A few things you should note here. First off - note that you get a nodeindex and value property. Secondly - the nodeindex is 0 based. In the screen shot above, I had clicked on the 4th bar, so the nodeindex returned 3.
So this raises a question. Assuming you want to "drill down" into details, how do we go from a simple index to the detail we want? Interestingly enough you have the same issue with "old" cfcharts too. (See this blog entry as an example.)
In order to correctly handle the click, you need to ensure that you can associate a nodeindex with the Nth (well, Nth+1) record from your chart. Given a query, that would be simple. You could repeat your query on the detail page and simply work with the Nth+1 row. (Of course, that's also a bit sloppy. If you use MySQL, you can pretty easily grab a particular row without returning the entire result set.)
If you are working with static data, then you would simply ensure that you handle the look up manually. I updated my JavaScript code to handle the "push" to the new location:
handle.node_click = function(dataOb) {
location.href='detail.cfm?datakey='+dataOb.key;
}
And then wrote code to handle the lookup:
<cfparam name="url.datakey" default="">
<cfswitch expression="#url.datakey#">
<cfcase value="0">
<cfset detail = "Apples">
</cfcase>
<cfcase value="1">
<cfset detail = "Bananas">
</cfcase>
<cfcase value="2">
<cfset detail = "Cherries">
</cfcase>
<cfcase value="3">
<cfset detail = "Donuts">
</cfcase>
<cfdefaultcase>
<cfset detail = "Apples">
</cfdefaultcase>
</cfswitch> <cfoutput>
Here are the details on #detail#
</cfoutput>
You can demo this yourself by clicking the lovely demo button below. I've also included the entire source for the chart demo at the bottom.
<html>
<head>
<script>
var handle;
function init() {
handle = ColdFusion.Chart.getChartHandle(); handle.node_click = function(dataOb) {
console.dir(dataOb);
location.href='detail.cfm?datakey='+dataOb.key;
}
}
</script>
</head> <body> <cfchart id="main" chartheight="500" chartwidth="500" title="Test Chart" format="html" scalefrom="50" scaleto="150" > <cfchartseries type="bar" serieslabel="Sales">
<cfchartdata item="Apples" value="120">
<cfchartdata item="Bananas" value="84">
<cfchartdata item="Cherries" value="72">
<cfchartdata item="Donuts" value="109">
</cfchartseries> </cfchart> <div id="data"></div> </body>
</html>
<cfset ajaxOnLoad("init")>