Introducing DojoX DataChart

March 30th, 2009 - by mwilcox

The latest addition in the Dojo 1.3 release is the new dojox.charting class, DataChart. Its primary purpose is to make connecting a chart to a Data Store a simple process. There are also other benefits with DataChart: less parameters are needed to create a basic chart, and more defaults and convenience methods get you up and running quickly with Dojox Charting.

The Data Store

Creating a chart is fast and easy, but before we can do so, we need our data. We’ll use dojo.ItemFileWriteStore, since it is the most commonly used, and most standard store. We’ll connect to the dojox/charting/tests/stock.json file which contains a set of imaginary stock market prices. The following code should connect to that JSON file and log all of the store items to the console:

dojo.require("dojo.data.ItemFileWriteStore");
       
var store = new dojo.data.ItemFileWriteStore({
        url:dojo.moduleUrl("dojox.charting", "tests/stock.json")
});
// testing
store.fetch({query:{name:"*"}, onComplete: function(items){
        console.log("fetched:", items);
}});
 

Fast Chart Setup

Now that we have our Data Store, we can create our chart. Start with an HTML node, formatted to the size you want the chart to be. I’m sizing the node inline, but you could use a style and class name just as well:

<div id="chartDiv" style="width: 400px; height: 250px;"></div>
 

We naturally have to wait for the DOM to load, so in dojo.addOnLoad, we’ll place the chart code. In its most basic, default form, it requires no parameters, besides the node id. The store can be set in the parameters or as a separate method as shown. The setStore method is the same as used elsewhere in Dojo, like in the DataGrid. Unlike the Grid, the DataChart needs one additional argument — the store item property that will be charted. In this case, that property is historicPrice.

dojo.require("dojox.charting.DataChart");
dojo.addOnLoad(function(){
        chart = new dojox.charting.DataChart("chartDiv", {});
        chart.setStore(store, {symbol:"*"}, "historicPrice");
});
 

Chart01

And that’s all it takes to get up and running with charts.

DataChart Mapping

Now let’s look at a (trimmed down) version of the JSON file we are using, as it is important to the next concepts we will discuss:

{ "identifier": "symbol", "idAttribute":"symbol", "label": "symbol","items": [
        { "symbol":"ANDT",
                "historicPrice":[0.01,3.52,3.66,3.11,3.90,3.11,3.11], "price":3.52},
        { "symbol":"ATEU",
                "historicPrice":[6.72,6.76,6.61,6.41,6.31,6.99,7.20], "price":6.76},
        { "symbol":"BGCN",
                "historicPrice":[4.11,3.98,4.05,4.20,4.16,4.22,3.80], "price":3.98},
        { "symbol":"BAYC",
                "historicPrice":[9.79,9.60,9.50,2.23,9.45,9.76,9.99], "price":9.60},
        { "symbol":"CRCR",
                "historicPrice":[8.44,8.44,8.54,8.60,9.65,8.42,8.44], "price":8.44},
        { "symbol":"DTOA",
                "historicPrice":[2.11,2.47,3.11,3.06,3.01,3.01,3.00], "price":2.47}
]}
 

The property we used for our default chart is historicPrice and as you can see, it’s an array. Because the default chart is a line-type, and plots each item separately, more than one bit of data is necessary to span across the x-axis. In our case, there are seven slots in the array, which is why we had an x-axis span of seven. If we charted the price property, we would have seven points all along the y-axis, and the chart x-axis would only have a length of one.

However, the need of the chart may not be a series of individual item properties, it may be single item properties compared to the others. We can compare the price of each item on a line chart using the comparative property:

dojo.addOnLoad(function(){
        chart = new dojox.charting.DataChart("chartDiv", {
                comparative:true
        });
        chart.setStore(store, {symbol:"*"}, "price");  //  <– single value property
});
 

Chart02

Chart Labels

Those x-axis labels could stand to be a little more descriptive. Fortunately, DataChart has a convenient method for applying the store item labels. We can edit the chart.xaxis object, which is exposed so that besides the property we will add here, you can edit the standard chart axis properties as shown in the comments of the code.

labelFunc is actually a method that you can tap into to write custom labels. In our case, we are going to give it a string, which tells DataChart to use its seriesLabels method. Simply put, it connects the item labels to the chart labels.

dojo.addOnLoad(function(){
        chart = new dojox.charting.DataChart("chartDiv", {
                comparative:true,
                xaxis:{labelFunc:"seriesLabels"},
        });
        chart.setStore(store, {symbol:"*"}, "price");
});
 

Chart03

Chart Legends

Let’s backtrack for a minute here. What about that first chart we created? The x-axis in that case more closely represented time, not the items (and if you wanted to put different times on the x-axis you could write a custom label function that did that using xaxis.labelFunc — but that is beyond the scope of this article). What we need now is a legend. DojoX Charting comes with dojox.charting.widget.Legend to make it easy to add legends to your charts. Currently however, it isn’t designed to handle “live” data — it’s expecting that the chart knows the data as it’s created. It doesn’t mean we can’t use it, we just need a little extra code to connect it when the chart has data:

<div id="chartDiv" style="width: 400px; height: 250px;"></div>
<div id="legend"></div>
 
dojo.addOnLoad(function(){
        chart = new dojox.charting.DataChart("chartDiv", {});
        chart.setStore(store, {symbol:"*"}, "historicPrice");
       
         var c = dojo.connect(chart, "onData", function(){
                dojo.disconnect(c);
                new dojox.charting.widget.Legend({chart:chart}, "legend");
        });
});
 

Chart04

In the previous example, we connected to the chart’s onData event (we connected once by disconnecting it immediately after the first call) and then create the legend programatically, now that the chart knows its data.

Different Chart Types

So what about other chart styles? It’s a simple matter of passing a type:

dojo.addOnLoad(function(){
        chart = new dojox.charting.DataChart("chartDiv", {
                type: dojox.charting.plot2d.Columns, // <– chart type
                scroll:false,
                xaxis:{labelFunc:"seriesLabels"}
        });
        chart.setStore(store, {symbol:"*"}, "price");
});
 

Chart05

Hey, wait a minute! What is scroll:false?

Animation

The DataChart supports information updating, which allows it to animate. To update the information, you change the item property in the store. The DataChart is connected to the onSet event in the store and reflects the change. The test in the DojoX trunk demonstrates this with spinner widgets that modify the store item property.

The scroll parameter works in conjunction with two other parameters:

  • stretchToFit: This is how native charts work. If given five bits of data, the x-axis will be five segments long.
  • displayRange: If set, stretchToFit is overridden and the x-axis will not stretch, it will always be that long. This is often used with scroll.
  • scroll: If true and there is more data than fits on the x-axis, it will scroll to the left.

Note that the y-axis is always fixed, having it stretch to fit or scroll is not currently supported.

Demo

I know that the combination of these parameters can be confusing, especially when it comes to different chart types. I’ve created a demo that you can use to test the different parameters and dynamically build new charts. It not only shows off the features of DataChart, but will get you up and running that much faster!

Note: Learn more about the new DojoX Data Chart and more at one of our upcoming workshops in Sydney, Munich, London, Milan, or Paris.

Bookmark and Share

Tags: , , , , , , , ,

25 Responses to “Introducing DojoX DataChart”

  1. [...] DataChart provides that interface between the two, all while supporting Dojo Data. I did a full write-up of DataChart in a previous post. The intent was to make DataChart as easy to setup as the DataGrid: <div [...]

  2. Thomas says:

    Does this work with the dojox.data.XmlStore ?

  3. mwilcox says:

    @Thomas:
    It should. The idea is that when you get components to work with ItemFileWriteStore, they should work with all the others.

  4. Thomas says:

    @mwilcox: Is there a concrete example for connecting this component to xml stores? I am having trouble getting the DataChart component working with the dojox.data.XmlStore.

  5. mwilcox says:

    @Thomas:
    So I looked into xmlStore, which I had never used before. It seems to have it’s own interface – returning xmlItems instead of numbers or strings. There’s an attributeMap that might help. I’m not versed on xmlStore, so if you need help on that, please look to the forums.

    As for DataChart, I fortunately made a convenience method that grabs values: getProperty(item, prop)

    You can overwrite this method and change it up to do the double-get that the xmlStore seems to require:

    var val = store.getValue(item, prop);
    return store.getValue(val, “text()”);

  6. Conar says:

    is there a way to have the grid self configure to an ever changing datastore? I would like to set it up so that there can be a dropdown to select different chart options (pie, lines, etc.) and have it grab its data either from selected rows, range of cells, the whole datastore, or even better to be able to choose between these options.

  7. Prog says:

    How can I get the xaxis labels from the json string (not compared)?
    Can you give me a short example, because I want do get the data including the labels from a database.

  8. mwilcox says:

    Prog,
    You can set the labels directly as in the charting/test_DataChart.html:
    xaxis:{labels:["0", "A","B","C","D","E","F","G","H","I","J"]}

    Or you you can make a custom function if the need to update them:
    xaxis:{labelFunc:myCustomFunction}

  9. Prog says:

    Thanks,
    it works with the following code:
    xaxis:{labels:colorData.mylabel}
    // colorData is the JSON I get from the database

  10. cédric says:

    I have an x-axis that takes 1000 to value. How can i change the range of this axis?

  11. cédric says:

    Sorry, I found the answer:
    In fact it was about the y-axis and i had to add option:
    {max:1000}

  12. dagarwal says:

    I was able to integrated the DataChart in my app successfully but it works for Mozilla and Safari only. Its frustrating that it doesn’t work with IE (checked with 6, 7, 8). Tried viewing https://user.sitepen.com/~mwilcox/Chart/DataChart.html sample page in IE and the problem is visible. Is it something which can be fixed in nightly or is it some significant amount of work?

  13. mwilcox says:

    @dagarwal:

    I found the problem. You need to remove the DOCTYPE from the top of the document. This screws up IE8 in particular, but it doesn’t work well with any vector graphics.

    I found a few minor things wrong which I’ll commit (and I’ll remove the DOCTYPE from the tests).

  14. Carl says:

    Is it possible to plot multiple data series from the same datastore on one chart? For example, if I have a datastore such as:

    { “identifier”: “id”, “idAttribute”:”id”, “label”: “Date”,”items”: [
    {
    "id": 1,
    "Date":"2009-01-01",
    "HitsFromFireFox":46,
    "HitsFromSafari":25,
    "HitsFromIE": 120
    }
    ]}

    ….could I plot the hits from the various browsers as three data series on one chart over time?

  15. mwilcox says:

    @Carl:

    I’m sorry there’s currently no easy way to do this without changing your data. But it a feature that should be in there so I’ve created an enhancement ticket for it:
    http://bugs.dojotoolkit.org/ticket/9739

  16. Andy says:

    Is horizontal scrolling working for anyone?

    I have 24 data points and set

    displayRange:10,
    scroll: true

    The displayRange works, but no scrolling action.
    Am I missing something?

  17. mwilcox says:

    Try stretchToFit, comparative and displayRange, like shown in the demo:
    https://user.sitepen.com/~mwilcox/Chart/DataChart.html

  18. Jeff says:

    My HTML file can be accessed from:
    “http://userwww.sfsu.edu/~jharan/agis_gmaps/dataChart.html”

    I’m just now learning dojo via the ArcGIS Javascript API, so please bear with me….

    When I try the above example by loading the stocks.json from my local machine and loading dojo via ESRI’s CDN (http://serverapi.arcgisonline.com/jsapi/arcgis/?v=1.5) firebug throws the following exception:

    “Access to restricted URI denied” code: “1012″ nsresult: “0×805303f4 (NS_ERROR_DOM_BAD_URI)” location: “http://serverapi.arcgisonline.com/jsapi/arcgis/?v=1.5 Line: 16″] File: Line: 0 Column: 0

    I believe this is a result of the cross domain issue. Is it looking for the JSON file in the CDN?
    Do I need to use JSONP or some other cross domain method to access the JSON file from “dojox/charting/tests/stock.json”?

  19. mwilcox says:

    HI Jeff,

    Yes, you have two different domains going there and that won’t work. JSONP should get around it.

  20. wirelessdreamer says:

    Could you add an example to use the function that converts from an array of labels to labels.

    example: for the first chart on https://user.sitepen.com/~mwilcox/dojotoolkit/dojox/charting/tests/test_DataChart.html. Instead of specifying the labels when the chart was programatically created, extract x axis names, and the y value ranges from the datastore.

    I can’t find the right way to apply these labels and set the y range for the chart.

    example json:

    {

    “idAttribute” : “symbol”,

    “identifier” : “symbol”,

    “label” : “symbol”,

    “items” : [

    {

    "count" : [

    150,

    150,

    300,

    400,

    300,

    800,

    700,

    800,

    706,

    794,

    734,

    766,

    753,

    747,

    775,

    728,

    797,

    721,

    779,

    644,

    761,

    740,

    794,

    725,

    776,

    739,

    765,

    767,

    737,

    777,

    736,

    236

    ],

    “uniquename” : “123456789-test-15-5″,

    “symbol” : “123456789″,

    “name” : “123456789″,

    “name” : “test”,

    “current” : 236,

    “labels” : [

    "15:5",

    "15:6",

    "15:7",

    "15:8",

    "15:9",

    "15:10",

    "15:11",

    "15:12",

    "15:13",

    "15:14",

    "15:15",

    "15:16",

    "15:17",

    "15:18",

    "15:19",

    "15:20",

    "15:21",

    "15:22",

    "15:23",

    "15:24",

    "15:25",

    "15:26",

    "15:27",

    "15:28",

    "15:29",

    "15:30",

    "15:31",

    "15:32",

    "15:33",

    "15:34",

    "15:35",

    "15:36"

    ],

    “yaxis” : 880

    }

    ]

    }

  21. mwilcox says:

    @ wirelessdreamer

    I think what you are asking for is the labelFunc method in the yaxis:

    chartLines = new dojox.charting.DataChart(”lines”, {
    type: dojox.charting.plot2d.Markers,
    yaxis:{
    labelFunc: “seriesLabels”
    }
    });

    “labelFunc” expects a function normally, “seriesLabels” is a DataChart-specific helper function. You could also do something custom:

    chartLines = new dojox.charting.DataChart(”lines”, {
    type: dojox.charting.plot2d.Markers,
    yaxis:{
    labelFunc: function(axisNumericalPosition){
    // get store element or property here, based on the axisNumericalPosition, which
    // will fire for each element or “tick” of your axis. If you just return it, it would be
    // 0,1,2,3,4… etc.
    }
    }
    });

  22. Chandan says:

    Is there a way to build a chart similar to the one on itunes – when you select your device (iphone/ipod) a single horizontal bar chart appears which splits the total capacity based on the space consumed by components like audio,photos,apps etc

  23. mwilcox says:

    Chandan, that would be StackedBars or StackedColumns. I’m not sure if that works with DataChart… I think it should. If not please file an enhancement request. See the charting tests for 2DChart.

  24. Chandan says:

    Thanks mwilcox, I will try that. I have a different question this time -

    Currently the legend widget for my data chart (Pie) always displays percentages. How can I make this to display stock “price”. I have the below script in my addOnLoad function. My html had “legend_pie” div.

    var store = new dojo.data.ItemFileWriteStore({
    url:dojo.moduleUrl(”", “../pages/users/data/stock.json”)
    });
    var dc = dojox.charting;
    Chart = new dc.DataChart(”chartDiv”, {
    type: dojox.charting.plot2d.Pie, // < chart type
    comparative:true
    });

    Chart.setStore(store, {symbol:"*"}, "price"); // < — single value property
    var anim_a = new dc.action2d.MoveSlice(Chart, "default");
    var anim_b = new dc.action2d.Highlight(Chart, "default");
    var anim_c = new dc.action2d.Tooltip(Chart, "default");
    Chart.render();
    var legend_pie = dojo.connect(Chart, "onData", function(){
    dojo.disconnect(legend_pie);
    new dojox.charting.widget.Legend({chart:Chart}, "legend_pie");
    });

  25. mwilcox says:

    Hi Chandan,

    Sorry, it looks like percentages is baked into the Pie Chart and/or the Legend. The only way to change this is to change the code. You could overwrite either Legend.refresh or Legend._addLabel, copying the code and editing it to suit your needs. You could also attach to it:

    myLegend._oldAddLabel = myLegend._AddLabel

    myLegend._AddLabel = function(dyn, label){
    // strip the % off of label and reinsert it
    }

    But I’m afraid either way is kludgey.

Leave a Reply