B. Clay Shannon B. Clay Shannon - 2 months ago 21
Javascript Question

How can I append a calculated value to the end of a bar in Chart.JS?

I'm generating a horizontal bar graph that looks like this:

enter image description here

I need it to look something like this:

enter image description here

That is to say, I need the percentage difference between week 1 (the top/green bar) and week 2 (the bottom/orange bar) to be appended to the week 2 bars.

I'm adding the vals to the bars themselves with this code:

Chart.pluginService.register({
afterDraw: function (chartInstance) {
var ctx = chartInstance.chart.ctx;
// render the value of the chart above the bar
ctx.font = Chart.helpers.fontString(14, 'bold', Chart.defaults.global.defaultFontFamily);
ctx.textAlign = 'center';
ctx.textBaseline = 'bottom';

chartInstance.data.datasets.forEach(function (dataset) {
for (var i = 0; i < dataset.data.length; i++) {
var model = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._model;
ctx.fillText(addCommas(dataset.data[i]), model.base + 20, model.y + 6);
}
});
}
});


...but don't know how to make a calculation and append that to the end of the bars.

For the full "monty" here's the rest of the jQuery for this chart:

var ctxForecastChart = $("#forecastLineChart").get(0).getContext("2d");
var forecastChartData = {
labels: [
"Total Qty", "Total Sales"
],
datasets: [
{
label: "9/18/2016",
backgroundColor: "rgba(34,139,34,0.75)",
hoverBackgroundColor: "rgba(34,139,34,1)",
data: [100, 1000.00]
},
{
label: "9/25/2016",
backgroundColor: "rgba(255,153,51,0.75)",
hoverBackgroundColor: "rgba(255,153,51,1)",
data: [110, 1110.11]
}
]
};

var optionsForecast = {
tooltips: {
enabled: true
}
};

var forecastBarChart = new Chart(ctxForecastChart,
{
type: 'horizontalBar',
data: forecastChartData,
options: optionsForecast
});


UPDATE



Tektiv's code looks very promising, but for some reason it completely breaks my charts. With my code:

Chart.pluginService.register({
afterDraw: function (chartInstance) {
var ctx = chartInstance.chart.ctx;
// render the value of the chart above the bar
ctx.font = Chart.helpers.fontString(14, 'bold', Chart.defaults.global.defaultFontFamily);
ctx.textAlign = 'center';
ctx.textBaseline = 'bottom';

chartInstance.data.datasets.forEach(function (dataset) {
for (var i = 0; i < dataset.data.length; i++) {
var model = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._model;
ctx.fillText(addCommas(dataset.data[i]), model.base + 20, model.y + 6);
}
});
}
});


...I see this:

enter image description here

With the new code replacing that above:

Chart.pluginService.register({
afterDraw: function (chartInstance) {
var ctx = chartInstance.chart.ctx;
ctx.font = Chart.helpers.fontString(14, 'bold', Chart.defaults.global.defaultFontFamily);

// `start` makes a better rendering IMO
ctx.textAlign = 'start';
ctx.textBaseline = 'bottom';
ctx.fillStyle = '#666';

chartInstance.data.datasets.forEach(function (dataset) {
for (var i = 0; i < dataset.data.length; i++) {
var model = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._model;
ctx.fillText(dataset.data[i], model.base + 5, model.y + 6);

if (i == 1) {
// `Total Sales` values here

// If needed, here you get the other dataset
var otherDataset = chartInstance.data.datasets[(dataset._meta[0].controller.index == 1) ? 0 : 1];

// Get the value you want to display
var value = Math.round((Math.abs(dataset.data[i] - otherDataset.data[i]) / dataset.data[i]) * 100);

// Display it with the `x` property of the model
ctx.fillText(value + "%", model.x + 5, model.y + 6);
}
}
});
}
});


...I see this:

enter image description here

I have no idea why - the new code looks fine, AFAICT.

BTW, after today, I will be on vacation until Tuesday the 27th, so won't be able to Bountify this question until then, but will do so ASAP.

UPDATE 2



The updated code is a little better, but it still torques the other charts:

enter image description here

UPDATE 3



Apparently it's a problem of dueling OnDraw() events; I also have this one aimed at the Price Compliance chart:

Chart.pluginService.register({
afterDraw: function (chartInstance) {
var ctx = chartInstance.chart.ctx;
// render the value of the chart above the bar
ctx.font = Chart.helpers.fontString(14, 'bold', Chart.defaults.global.defaultFontFamily);
ctx.textAlign = 'center';
ctx.textBaseline = 'bottom';

chartInstance.data.datasets.forEach(function (dataset) {
for (var i = 0; i < dataset.data.length; i++) {
var model = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._model;
ctx.fillText(addCommas(dataset.data[i]), model.base + 20, model.y + 6);
//ctx.fillText(dataset.data[i], model.base + 20, model.y + 6);
}
});
}
});

Answer

As you did with model.base when you are adding the data, do the following :

Chart.pluginService.register({
    afterDraw: function(chartInstance) {
        var ctx = chartInstance.chart.ctx;
        ctx.font = Chart.helpers.fontString(14, 'bold', Chart.defaults.global.defaultFontFamily);

        // `start` makes a better rendering IMO
        ctx.textAlign = 'start';
        ctx.textBaseline = 'bottom';
        ctx.fillStyle = '#666';

        chartInstance.data.datasets.forEach(function(dataset) {
            for (var i = 0; i < dataset.data.length; i++) {
                var model = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._model;
                ctx.fillText(dataset.data[i], model.base + 5, model.y + 6);

                if (dataset._meta[0].controller.index == 1) {
                    // Orange bar (2nd dataset) values here

                    // We get the other dataset
                    var otherDataset = chartInstance.data.datasets[(dataset._meta[0].controller.index == 1) ? 0 : 1];

                    // Then calculate the percentage difference
                    var value = Math.round((Math.abs(dataset.data[i] - otherDataset.data[i]) / otherDataset.data[i]) * 100);

                    // And finally we display it
                    ctx.fillText(value + "%", model.x + 5, model.y + 6);
                }
            }
        });
    }
});


You can see the plugin working in this jsFiddle, and here is how it looks like :

enter image description here