AEgman AEgman - 7 days ago 4
Javascript Question

Dojo Stacked Bars/Columns chart labeling in incorrect position

I've come across an issue with the labeling of stacked bar and column charts in the latest versions of Dojo.
The labels are being placed in the centre of the bar/column as it is drawn from 0, not from the end of the previous bar/column. This means labels frequently overlap in the middle of the bar/column, or appear in the wrong section of the bar/column.




EXAMPLE: Borrowing the JS fiddle from this StackExchange post to illustrate the issue: change the Dojo version between "1.10.4" and "nightly" and you will see the label positions move.




I've located the reason for the issue, the "GetValue" function has been removed from the StackedBars and StackedColumn (Dojox\charting\plot2d\StackedColumn.js) files between versions 1.10.6 and 1.11.1. This function used the previous bar/column value to adjust the label position, as its now missing this doesn't happen.

I'm using ESRIs JavaScript API, which includes Dojo 1.11.1. I've tried copying (and modifying) the function from the older version of dojo, but it appears many of the other charting functions have changed and I've not been successful in getting it working.

I'm still fairly new to JavaScript and Dojo, but is there anyway to add this function back in without modifying the files in the ESRI API?

Failing that is there a way to retro-fit the old function in to the newer version successfully?

Thanks!




EDIT: This is the contents of my modified StackedBars.js:

//>>built
define("dojox/charting/plot2d/StackedBars",["dojo/_base/declare","dojo/_base/lang","./Bars","./commonStacked"],function(c,e,f,d){
return c("dojox.charting.plot2d.StackedBars",f,{
getSeriesStats:function(){
var a=d.collectStats(this.series,e.hitch(this,"isNullValue")),b;
a.hmin-=0.5;
a.hmax+=0.5;
b=a.hmin;
a.hmin=a.vmin;
a.vmin=b;
b=a.hmax;
a.hmax=a.vmax;
a.vmax=b;
return a
},
rearrangeValues:function(a,b,c){
return d.rearrangeValues.call(this,a,b,c)
},
// COPIED THIS FUNCTION FROM 1.10.6
getValue:function(_5,_6,_7,_8){
var y,x;
if(_8){
x=_6;
y=d.getIndexValue(this.series,_7,x,e.hitch(this, "isNullValue"));
}
else{
x=_5.x-1;
y=d.getValue(this.series,_7,_5.x);
y=[y[0]?y[0].y:null,y[1]?y[1]:null];
}
return {x:x,y:y[0],py:y[1]};
}
})});


My initial issue was with "e.hitch(this, "isNullValue")", I had made a typo.

I still have an issue with this not working for charts with negative values (no errors, just labels in the wrong place), and how to apply this function without modifying the Dojo source files?

Answer

The solution to getting labels correctly positioned on Dojo StackedBars and StackedColumns charts is to create a new class that inherits from dojox/charting/plot2d/StackedBars or dojox/charting/plot2d/StackedColumns and includes the missing GetValue function.

Here is a working class for StackedBars:

define(["dojo/_base/declare", "dojox/charting/plot2d/StackedBars", "dojox/charting/plot2d/commonStacked", "dojo/_base/lang"], 
function(declare, StackedBars, commonStacked, lang){

return declare("FixedStackedBars", dojox.charting.plot2d.StackedBars, {
    getValue: function(value, index, seriesIndex, indexed){
        var y,x;
        if(indexed){
            x = index;
            y = commonStacked.getIndexValue(this.series, seriesIndex, x, lang.hitch( this, "isNullValue" ) );
        }else{
            x = value.x - 1;
            y = commonStacked.getValue(this.series, seriesIndex, value.x);
            y = [  y[0]?y[0].y:null, y[1]?y[1]:null ];
        }
        // in py we return the previous stack value as we need it to position labels on columns
        return { x: x, y: y[0], py: y[1] };
    }
});
});

Use this new class in place of dojox/charting/plot2d/StackedBars in your code.

This method isn't perfect however, as it won't take in to account older or future versions of Dojo that may already include the GetValue function in the StackedBar/StackedColumn classes. Some mechanism to check if the base class includes the GetValue method is required.

It does work with the ESRI JavaScript API 3.17 and 3.18, for which it is intended.