Thursday 6 February 2014

ColdFusion-UI-the-Right-Way: <cfchart>

G'day:
Yay. I've finally participated in an open source project on Github. I've done a "chapter" on "ColdFusion UI the Right Way", covering <cfchart>. Well: using ZingCharts directly instead of <cfchart>. An easy win for me for today's article is to repeat the chapter here.



<cfchart>

The <cfchart> tag provides the ability to create charts in CFML. As a very minimal example:

Listing 1 : min.cfm

<cfchart format="html">
    <cfchartseries type="Bar" label="Numbers">
         <cfchartdata item="e" value="2.71828">
        <cfchartdata item="&pi;" value="3.14159">
    </cfchartseries>
</cfchart>



<cfchart> can implement charts as images in JPG or PNG format, as well as Flash, and - the focus of this chapter - JS/CSS/HTML. In versions of ColdFusion from 6.x to 9.x the chart engine was restricted to server-side-generated JPG/PNG/Flash-based charts; ColdFusion 10 added client-side charting capabilities.

For this chapter we will make use of the ZingCharts library, which is the same library ColdFusion uses. However we will simply use ZingCharts directly. Coincidentally the examples will also use jQuery to provide AJAX access to the data for the example, but this is just to demonstrate separation of model and view concerns, and is not a requirement of ZingCharts itself. Please note that only ColdFusion 10 enterprise includes licensing for ZingCharts; if one is not using that version, a separate licence will be required from ZingCharts. For unlicensed developmental purposes ZingCharts is fully-functional, but includes a watermark, which is removed once a licence is applied.

As a baseline, here is how one might implement a quick stacked bar chart with <cfchart>:

Listing 2 : cfchart.cfm

<cfset records = new DAO().getDataForCfmlVersion()>
<cfset seriesColours = ["Green","Yellow","Purple"]>
<cfset seriesColour = 1>

<cfchart chartwidth="1000" chartheight="750" seriesplacement="stacked" format="html">
    <cfloop query="records" group="item">
        <cfchartseries type="Bar" label="#item#" seriescolor="#seriesColours[seriesColour++]#">
            <cfloop>
                <cfchartdata item="#day#" value="#total#">
            </cfloop>
        </cfchartseries>
    </cfloop>
</cfchart>

With the data returned from the DAO, this renders:


Now here is an analogous chart, totally written in simple HTML and JavaScript.

Listing 3 : zingChart.html

<!doctype html>
<html>
    <head>
        <script type="text/javascript" src="lib/js/zingchart_trial/html5_scripts/zingchart-html5-min.js"></script>
        <script type="text/javascript" src="http://code.jquery.com/jquery-2.1.0.min.js"></script>
    </head>
    <body>
        <div id="myChartDiv"></div>
    </body>
    <script type="text/javascript" src="lib/js/renderChart.js"></script>
</html>

Listing 4 : renderChart.js

$(document).ready(function(){
    $.getJSON(
        "DAO.cfc?method=getDataForJSVersion",
        function(data){
            applyLabels(data.records);
            zingchart.render(configureChartParams(data));   
        }
    );

    var applyLabels = function(records){
        seriesColours = ["Green","Yellow","Purple"];
        records.forEach(function(series, index){
            series["background-color"] = seriesColours[index];
        });
        return records;
    };

    var configureChartParams = function(data){
        return {
            id      : "myChartDiv",
            height  : 750,
            width   : 1000,
            data    : myChart = {
                type    : "bar",
                stacked : true,
                series  : data.records,
                "scale-x"   : {
                    values:data.label
                },
                legend      : {}
            }
        };  
    }

});

As you can see, the only mark-up needed is the myChartDiv <div>. The rest is done with JavaScript. It might seem like the JavaScript is complicated, but it boils down to a call to zingchart.render(). passing it an object containing data and config. As I said above I am using AJAX here to fetch the raw data from the server, but that is not a requirement, it could just as easily be inline JavaScript. Here's the render:



The styling is not exactly the same, but that's due to ColdFusion using different "default" styling than ZingCharts does.

For completeness, here is the code for DAO.cfc:

Listing 5 : DAO.cfc


component {

    remote struct function getDataForJSVersion() returnformat="json" {
        return {
            "labels"    = "Mon,Tue,Wed,Thu,Fri",
            "records"    = [
                {
                    "text"    ="Apples",
                    "values"=[1,2,4,8,4]
                },{
                    "text"    ="Bananas",
                    "values"=[10,8,6,4,6]
                },{
                    "text"    ="Cherries",
                    "values"=[1,3,9,3,1]
                }
            ]
        };
    }

    public query function getDataForCfmlVersion(){
        return queryNew(
            "id,day,item,total",
            "integer,varchar,varchar,integer", [
                [1, "Mon", "Apples", 1],
                [4, "Tue", "Apples", 2],
                [7, "Wed", "Apples", 4],
                [10, "Thu", "Apples", 8],
                [13, "Fri", "Apples", 4],
                [2, "Mon", "Bananas", 10],
                [5, "Tue", "Bananas", 8],
                [8, "Wed", "Bananas", 6],
                [11, "Thu", "Bananas", 4],
                [14, "Fri", "Bananas", 6],
                [3, "Mon", "Cherries", 1],
                [6, "Tue", "Cherries", 3],
                [9, "Wed", "Cherries", 9],
                [12, "Thu", "Cherries", 3],
                [15, "Fri", "Cherries", 1]
            ]
        );
    }
}

Resources


That's it. We want to keep these things short and sweet: offer an alternative, not an exhaustive list of options, or demonstrating every single compatibility consideration. The effort is not to emulate what CFML does exactly, but to point out there are easy options that just don't use CFML at all.

--
Adam