Chart.js: Combined Line and Bar Data

I want to create a Chart with Chart.js containing two different datasets: One Line Data Set and one Bar Data Set.

You can see my whole code for that here:

/**
 * 
 */
function initCombinedChart() {
    /**
     *
     */
    $("canvas").each(function() {
        var config = getConfigCombined($(this).attr("id"));
        var context = $(this);
        var combined = new Chart(context, config);
    });
}

/**
 * 
 * @param id
 * @returns {___anonymous464_473}
 */
function getConfigCombined(id) {
    var currentId = id;
    var currentIdNumber = currentId.substring((currentId.lastIndexOf("_") + 1), currentId.length);
    var entry = $("#" + id).data("entry");

    var labelMeasure = $("#evaluations_combined_measures").data("txt");
    var labelInsulin = $("#evaluations_combined_insulins").data("txt");

    var datasetLine = dataCombinedLine(labelMeasure, entry);
    var datasetCombined = dataCombinedBar(labelInsulin, entry);

    var config = {
        type: "bar",
        data: {
            labels: labelsFromEntry(entry),
            datasets: []
        },
        options: {
            responsive: true,
            title: {
                display: false
            },
            legend: {
                position: "bottom"
            },
            scales: {
                xAxes: [{
                    position: "bottom",
                    type: "time",
                    time: {
                        unit: "hour",
                        format: "HH:mm",
                        tooltipFormat: "HH:mm",
                        displayFormats: {
                            hour: "HH:mm",
                            day: "HH:mm",
                            week: "HH:mm",
                            month: "HH:mm",
                            quarter: "HH:mm",
                            year: "HH:mm"
                        }
                    },
                    gridLines : {
                        display : false
                    }
                }],
                yAxes: [{
                    type: "linear",
                    display: true,
                    position: "left",
                    id: "y-axis-0",
                    gridLines: {
                        show: true,
                    }
                }, {
                    type: "linear",
                    display: true,
                    position: "right",
                    id: "y-axis-1",
                    gridLines: {
                        show: false
                    }
                }]
            },
        }
    }

    if (datasetLine != null) {
        config.data.datasets.push(datasetLine);
    }

    if (datasetCombined != null) {
        config.data.datasets.push(datasetCombined);
    }

    return config;
}

/**
 * 
 * @param entry
 * @returns {Array}
 */
function labelsFromEntry(entry) {
    var result = [];
    var entryCombined;
    var entryMeasure;
    var entryInsulin;

    if (entry.indexOf("-") >= 0) {
        entryCombined = entry.split("-");
        entryMeasure = entryCombined[0];
        entryInsulin = entryCombined[1];
    } else {
        entryMeasure = entry;
        entryInsulin = "";
    }

    var entryMeasureArray = entryMeasure.split(";");
    var entryInsulinArray = entryInsulin.split(";");

    entryMeasureArray.forEach(function(entry) {
        var entryPair = entry.split(",");
        var date = parseFloat(entryPair[0]);
        var dateFormat = moment(date).format("HH:mm");

        if (!result.includes(dateFormat)) {
            result.push(dateFormat);
        }
    });

    entryInsulinArray.forEach(function(entry) {
        var entryPair = entry.split(",");
        var date = parseFloat(entryPair[0]);
        var dateFormat = moment(date).format("HH:mm");

        if (!result.includes(dateFormat)) {
            result.push(dateFormat);
        }
    });

    return result;
}

/**
 * 
 * @param label
 * @param entry
 * @returns {___anonymous3118_3127}
 */
function dataCombinedLine(label, entry) {
    var dataset = {
        type: "line",
        label: label,   
        lineTension: 0,
        backgroundColor: "#4078A7",
        borderCapStyle: "butt",
        borderJoinStyle: "miter",
        borderColor: "#4078A7",
        pointRadius: 5,
        pointBorderColor: "#4078A7",
        pointBackgroundColor: "#FFFFFF",
        pointBorderWidth: 3,
        pointHoverRadius: 5,
        pointHoverBackgroundColor: "#FFFFFF",
        pointHoverBorderWidth: 3,
        pointHitRadius: 5,
        data: dataCombinedLineFromEntries(entry),
        yAxisID : "y-axis-0",
        fill: false
    }

    return dataset;
}

/**
 * 
 * @param label
 * @param entry
 * @returns {___anonymous3299_3308}
 */
function dataCombinedBar(label, entry) {
    var dataset = {
        type: "bar",
        label: label,
        backgroundColor: "#239471",
        borderCapStyle: "butt",
        borderJoinStyle: "miter",
        borderColor: "#239471",
        data: dataCombinedBarFromEntries(entry),
        yAxisID : "y-axis-1"
    }

    return dataset;
}

/**
 * 
 * @param entry
 * @returns {Array}
 */
function dataCombinedLineFromEntries(entry) {
    var result = [];
    var entryMeasures = entry.split("-")[0];
    var entryMeasuresArray = entryMeasures.split(";");

    entryMeasuresArray.forEach(function(entry) {
        var entryPair = entry.split(",");
        var date = parseFloat(entryPair[0]);
        var value = entryPair[1];

        var data = {
            x: moment(date).format("HH:mm"),
            y: entryPair[1]
        }

        result.push(data);
    });

    return result;
}

/**
 * 
 * @param entry
 * @returns {Array}
 */
function dataCombinedBarFromEntries(entry) {
    var result = [];

    if (entry.indexOf("-") >= 0) {
        var entryInsulins = entry.split("-")[1];
        var entryInsulinsArray = entryInsulins.split(";");

        entryInsulinsArray.forEach(function(entry) {
            var entryPair = entry.split(",");
            var date = parseFloat(entryPair[0]);
            var value = entryPair[1];

            var data = {
                x: moment(date).format("HH:mm"),
                y: entryPair[1]
            }

            result.push(entryPair[1]);
        });
    }

    return result;
}

With this code i managed to put both datasets into one chart, but there are two problems left. The first problem is that the first and the last bar are overlapping with the yAxis:

enter image description here

How can I disable the overlap?

The second problem remaining is the following: The line data and the bar data arent always matching. That means: If i have a line data entry for 08:00 o'clock that does not mean that there is a bar data entry for 08:00 o'clock. Same counts the other way: if there is a bar data for 12:00 o'clock it does not mean that there is a line data matching for 12:00 o'clock. There can be X data entries for the line data but Y data entries for the bar data. For that i created x and y values for the line data:

    entryMeasuresArray.forEach(function(entry) {
        var entryPair = entry.split(",");
        var date = parseFloat(entryPair[0]);
        var value = entryPair[1];

        var data = {
            x: moment(date).format("HH:mm"),
            y: entryPair[1]
        }

        result.push(data);
    });

This is working perfectly for the line data. But unfortunately i could not find such an option for the bar data. The bar data does not accept x and y values, it only accepts the y value:

        entryInsulinsArray.forEach(function(entry) {
            var entryPair = entry.split(",");
            var date = parseFloat(entryPair[0]);
            var value = entryPair[1];

            var data = {
                x: moment(date).format("HH:mm"),
                y: entryPair[1]
            }

            result.push(entryPair[1]);
        });

So how can i define x and y values for the bar data as i can define them for the line data?

EIDT

Im using the latest version 2.1.6

EDIT 2

Here a JSFiddle to demonstrate the problems

Answers:

Answer

For the first problem :

How can I disable the overlap?

xAxes: [{
  position: "bottom",
  //type: "time",    // erase this line
  time: {
    unit: "hour",
    format: "HH:mm",
    tooltipFormat: "HH:mm",
    displayFormats: {
      hour: "HH:mm",
      day: "HH:mm",
      week: "HH:mm",
      month: "HH:mm",
      quarter: "HH:mm",
      year: "HH:mm"
    }
  }
}], 

In the XAxes erase the line type: "time", this option imply that the graph has to start in zero without margin.

The second problem:

I think the best way to represent your data:

var data = [
  { x: "08:00", y_line: "110", y_bar: "30"}, 
  { x: "09:00", y_line: "120", y_bar: "35"}, 
  { x: "10:00", y_line: "130", y_bar: null},
  { x: "11:00", y_line: "140", y_bar: 45}
];

With this representation you force that each "time" has two measurements, one for the bar and one for the line that can have a numerical value or a null value.

Then with some functions you can get the sets:

function getLine(){
    var arr = [];
    data.forEach(function(entry) { 
       var data = { x: entry.x, y: entry.y_line}
       arr.push(data);
    });
    return arr;
}

function getBar(){
    var arr = [];
    data.forEach(function(entry) { 
       arr.push(entry.y_bar);
    });
    return arr;
}

function getLabels(){
    var arr = [];
    data.forEach(function(entry) { 
       arr.push(entry.x);
    });
    return arr;
}

Then you introduce the values in your configuration like this:

{
 type: "bar",
 label: labelInsulin,
 backgroundColor: "#239471",
 borderCapStyle: "butt",
 borderJoinStyle: "miter",
 borderColor: "#239471",
 data: getBar(),           // like this
 yAxisID : "y-axis-1"
}

If a "bar" has value null it doesn't appear. And because the "bar" has the same number of elements than the "line" they always match.

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.