Adding click support in ColdFusion 10 Charting

This post is more than 2 years old.

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" >

&lt;cfchartseries type="bar" serieslabel="Sales"&gt;
	&lt;cfchartdata item="Apples"  value="120"&gt;
	&lt;cfchartdata item="Bananas" value="84"&gt;
	&lt;cfchartdata item="Cherries" value="72"&gt;
	&lt;cfchartdata item="Donuts" value="109"&gt;
&lt;/cfchartseries&gt;

</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" >

&lt;cfchartseries type="bar" serieslabel="Sales"&gt;
	&lt;cfchartdata item="Apples"  value="120"&gt;
	&lt;cfchartdata item="Bananas" value="84"&gt;
	&lt;cfchartdata item="Cherries" value="72"&gt;
	&lt;cfchartdata item="Donuts" value="109"&gt;
&lt;/cfchartseries&gt;

</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" >

&lt;cfchartseries type="bar" serieslabel="Sales"&gt;
	&lt;cfchartdata item="Apples"  value="120"&gt;
	&lt;cfchartdata item="Bananas" value="84"&gt;
	&lt;cfchartdata item="Cherries" value="72"&gt;
	&lt;cfchartdata item="Donuts" value="109"&gt;
&lt;/cfchartseries&gt;

</cfchart>

<div id="data"></div>

</body> </html> <cfset ajaxOnLoad("init")>

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Scott Stroz posted on 3/14/2012 at 7:14 PM

I know we 'discussed' this privately, but another option is to use the ZingChart events. The following code allows us to listen for when the chart is done doing its thing. We can then set handlers and such.

ColdFusion.Chart.getChartHandle("myChart").complete = function(jsonobj){}

For more on ZingChart events, check out the docs - http://www.zingchart.com/le...

Comment 2 by Aaron Neff posted on 3/22/2012 at 4:45 PM

This is just too cool. Adobe should implement this, and remove that restriction :) Thanks Ray and Scott.

Comment 3 by Jeffrey Mark Christie posted on 12/18/2013 at 2:18 AM

Please let me know if you can help. I need to be able to pass more information out of my chart than the value and node to properly redirect it to the proper detail report.

In the previous versions I used $SERIESLABEL$, $ITEMLABEL$ values to pass through the URL parameter to my handler page.

It looks like the option in this example is to be able to populate the section in the object labeled text. Do you have a suggestion as to how to dynamically per data point as my line graph is drawn to get my desired information into that part of the object?

Comment 4 by Raymond Camden posted on 12/18/2013 at 3:40 AM

"Do you have a suggestion..."

Um, outside of what I said in the blog entry? Maybe I don't understand your question, but did you carefully read the entry and how it works?

Comment 5 by Jitendra posted on 5/27/2014 at 6:13 PM

How can i get value of X- label value on click event. Like in your console.dir(dataOb);
you are getting value=109. so how can i get X label value like Donuts without using CFCASE?

Comment 6 by Raymond Camden posted on 5/27/2014 at 6:17 PM

You can't, afaik.

Comment 7 by Raymond Camden posted on 5/27/2014 at 6:25 PM

Actually - I could be wrong. The docs imply we can get it, but it isn't working for me, so I've asked zingcharts on Twitter.

Comment 8 by Raymond Camden posted on 5/29/2014 at 1:56 AM

So I dug into this, and I spoke w/ ZingCharts on twitter. They actually put together *3* demos for me trying to get this right. Issue ended up being that CF10 has an older version of ZingCharts. The same code in 11 will return the node title in the event handler.

So - long story short - yet another remind to skip using CF's built in UI stuff and use libraries like Zing directly.