Tenz
Tenz

Reputation: 555

Chart JS, Choose different Y-axis for different datasets

I have a multiple Y axis on my chart Codepen Example. I am wondering if user can choose which dataset he/she want on which y-axis? Is there a way to show which data is currently using which Y-axis, and users haveoption to change the data to different axis (left or right) if they like on the legend?

Chart.defaults.global.elements.line.fill = false;

  var barChartData = {
  labels: [1510332261000, 1510332473000, 1510332489000, 1510332726000, 1510332777000, 1510332778000, 1510332958000, 1510333050000, 1510333131000, 1510333389000, 1510333476000, 1510333493000, 1510333588000, 1510333604000, 1510333664000, 1510333668000, 1510333758000, 1510333801000, 1510333820000, 1510333821000, 1510333878000, 1510333928000],
  datasets: [{
  type: 'line',
  label: 'a (F)',
  id: "y-axis-0",
  backgroundColor: "rgba(217,83,79,0.75)",
  data: [70, 72, 73, 73, 75, 50, 50, 40, 40, 45, 70, 73, 70, 73, 73, 73, 74, 73, 73, 73]
}, {
  type: 'line',
  label: 'b (V)',
  id: "y-axis-0",
  backgroundColor: "rgba(92,184,92,0.75)",
  data: [12.9, 17.9, 15.9, 17.9, 17.9, 17.9, 15.9, 17.9, 15.8, 17.8, 16.8, 17.8, 17.9, 17.9, 17.8, 17.8, 19.8, 17.9, 17.8, 20.8]
}, {
  type: 'line',
  label: 'c (%)',
  id: "y-axis-0",
  backgroundColor: "rgba(51,51,51,0.5)",
  data: [30, 30, 50, 30, 20, 10, 30, 40, 30, 50, 30, 70, 30, 50, 30, 80, 90, 30, 30, 30]
}, {
  type: 'line',
  label: 'd (AH)',
  id: "y-axis-1",
  backgroundColor: "rgba(151,187,205,0.5)",
  data: [10.6, 10.6, 10.6, 10.6, 10.6, 10.6, 10.6, 10.6, 10.6, 10.6, 10.6, 10.6, 10.6, 10.6, 10.6, 10.6, 10.6, 10.6, 10.6, 10.6]
  }]
};


var ctx = document.getElementById("myChart");
// allocate and initialize a chart
var ch = new Chart(ctx, {
  type: 'bar',
  data: barChartData,
  options: {
    title: {
      display: true,
      text: "Chart.js"
    },
    tooltips: {
       mode: 'label'
    },
    responsive: true,
    scales: {
      xAxes: [{
        stacked: true
      }],
      yAxes: [{
        position: "left",
        id: "y-axis-0",
      }, {
        position: "right",
        id: "y-axis-1",
      }]
    }
  }
});
.myChartDiv {
  max-width: 600px;
  max-height: 400px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.1/Chart.min.js"></script>
<html>
  <body>
    <div class="myChartDiv">
      <canvas id="myChart" width="600" height="400"></canvas>
    </div>
  </body>
</html>


Update

I customized the legend as this, and use Ahmed 's suggestion code to fix Y-axis scale. I also added Ahmed's y axis code to the update, but somehow, it still doesn't work as I expect. Here is the new codepen that I just updated

Upvotes: 4

Views: 4155

Answers (1)

Ahmed Musallam
Ahmed Musallam

Reputation: 9753

Fist of all in your barChartData.datasets[] you are using id but you should use yAxisIDinstead. giving id there has no impact on the chart. here is the doc for that: http://www.chartjs.org/docs/latest/charts/line.html#dataset-properties

Now onto what you're asking, you can add a click handler to the legend to toggle between axes on click, here is an example of that:

a fork codepen off of yours with new code: https://codepen.io/anon/pen/ooZMwG?editors=0010

set options.legend.onClick to this function:

function(e, legendItem) {
    var index = legendItem.datasetIndex;
    var ci = this.chart;

    var axisIds = []
    var yAxes = ci.options.scales.yAxes;
    for (var i = 0; i < yAxes.length; i++) { // loop over the registered yAxes
        yAxes[i].gridLines.color = "rgba(0,0,0,0.1)"; // reset all scale gridlines to default
        axisIds.push(yAxes[i].id) // get all yAxes ids
    }

    var meta = ci.getDatasetMeta(index);
    var currentAxisId = meta.yAxisID
    var currentAxisIdIndex = axisIds.indexOf(currentAxisId);
    var newAxisIdIndex;
    // basically get the next axis id in the array, if the last one, get the first (circular)
    if (currentAxisIdIndex == axisIds.length - 1) newAxisIdIndex = 0;
    else newAxisIdIndex = currentAxisIdIndex + 1;

    var newAxisId = axisIds[newAxisIdIndex];
    meta.yAxisID = newAxisId // set the new axis id to the next one in the array

    axis = ci.scales[newAxisId];
    axis.options.gridLines.color = "rgba(0,0,0,1)"; // change lines color of the new axes 
    ci.update(); // update chart
}

**UPDATE: **

based on your new codepen, here are a few things to help:

I have added a new update to my answer to add offfset to all Y Axes in the chart. Use that instead of what you have in the pen.

then update your changeLeftRightY to the following and make sure your legend HTML container has the id: legend

changeLeftRightY = function(e, index) {
            //console.log("-- " + data.datasets[index].yAxisID)
            var ci = e.view.ch;
            var axisIds = []
            var yAxes = ci.options.scales.yAxes;
            for (var i = 0; i < yAxes.length; i++) { // loop over the registered yAxes
                yAxes[i].gridLines.color = "rgba(0,0,0,0.1)"; // reset all scale gridlines to default
                axisIds.push(yAxes[i].id) // get all yAxes ids
            }
            var meta = ci.getDatasetMeta(index);
            var currentAxisId = meta.yAxisID
            var currentAxisIdIndex = axisIds.indexOf(currentAxisId);
            var newAxisIdIndex;
            // basically get the next axis id in the array, if the last one, get the first (circular)
            if (currentAxisIdIndex == axisIds.length - 1) newAxisIdIndex = 0;
            else newAxisIdIndex = currentAxisIdIndex + 1;

            var newAxisId = axisIds[newAxisIdIndex];
            meta.yAxisID = newAxisId // set the new axis id to the next one in the array

            axis = ci.scales[newAxisId];
            axis.options.gridLines.color = "rgba(0,0,0,1)"; // change lines color of the new axes
            document.getElementById('legend').innerHTML = ci.generateLegend();

            ci.update()
        }

Here is a codepen with the code result *I have made some changes to some of your code to make it easier to read.

If that codepen is changed or deleted, here is the full code

// set default no fill beneath the line
Chart.defaults.global.elements.line.fill = false;
// Define a plugin to provide data labels
Chart.pluginService.register({
    beforeUpdate: function(chart){
      console.log("beforeUpdate");
      // get custom defined offset
      var offset = chart.options.customOffset;
      // exisit gracefully if offset not defined or less than 0
      if(!offset || offset < 0) return;
      var yAxes = chart.options.scales.yAxes;
      for(var i=0; i<yAxes.length; i++){
        var axis = yAxes[i];
        var datasets = chart.data.datasets;
        var max = Number.MIN_VALUE;
        var min = Number.MAX_VALUE;
        var datasetsOnAxis = [];

        for(var j=0; j<datasets.length; j++){ // add datasets for this axes to datasetsOnAxis
          var dataset = datasets[j];
          var meta = chart.getDatasetMeta(j);
          if  (meta.yAxisID === axis.id) datasetsOnAxis.push(dataset); 
        }

        for(var k=0; k<datasetsOnAxis.length; k++){
          var dataset = datasetsOnAxis[k]
          var newMax = Math.max.apply(null, dataset.data);
          var newMin = Math.min.apply(null, dataset.data);
          max = newMax > max ? newMax : max;
          min = newMin > min ? min : newMin;
        }
        axis.ticks.max = max + offset;
        axis.ticks.min = min - offset;   
      }
    }
});

var barChartData = {
  labels: [1510332261000, 1510332473000, 1510332489000, 1510332726000, 1510332777000, 1510332778000, 1510332958000, 1510333050000, 1510333131000, 1510333389000, 1510333476000, 1510333493000, 1510333588000, 1510333604000, 1510333664000, 1510333668000, 1510333758000, 1510333801000, 1510333820000, 1510333821000, 1510333878000, 1510333928000],
  datasets: [{
    type: 'line',
    label: 'a (F)',
    yAxisID: "y-axis-0",
    backgroundColor: "rgba(217,83,79,0.75)",
    data: [70, 72, 73, 73, 75, 50, 50, 40, 40, 45, 70, 73, 70, 73, 73, 73, 74, 73, 73, 73]
  }, {
    type: 'line',
    label: 'b (V)',
    yAxisID: "y-axis-0",
    backgroundColor: "rgba(92,184,92,0.75)",
    data: [12.9, 17.9, 15.9, 17.9, 17.9, 17.9, 15.9, 17.9, 15.8, 17.8, 16.8, 17.8, 17.9, 17.9, 17.8, 17.8, 19.8, 17.9, 17.8, 20.8]
  }, {
    type: 'line',
    label: 'c (%)',
    yAxisID: "y-axis-0",
    backgroundColor: "rgba(51,51,51,0.5)",
    data: [30, 30, 50, 30, 20, 10, 30, 40, 30, 50, 30, 70, 30, 50, 30, 80, 90, 30, 30, 30]
  }, {
    type: 'line',
    label: 'd (AH)',
    yAxisID: "y-axis-1",
    backgroundColor: "rgba(151,187,205,0.5)",
    data: [10.6, 10.6, 10.6, 10.6, 10.6, 10.6, 10.6, 10.6, 10.6, 10.6, 10.6, 10.6, 10.6, 10.6, 10.6, 10.6, 10.6, 10.6, 10.6, 10.6]
  }]
};

var options = {
  title: {
      display: true,
      text: "Chart.js"
    },
  legendCallback: function (chart) {
                console.log('legendCallback');
                var legendHtml = [];
                legendHtml.push('<div id="legend">');
                for (var i = 0; i < chart.data.datasets.length; i++) {
                    var meta = ch.getDatasetMeta(i);
                    var yAxis = meta.yAxisID =='y-axis-0' ? "Left Axis" : "Right Axis"

                    if (chart.data.datasets[i].label) {
                        var datasetIndex = chart.legend.legendItems[i].datasetIndex
                        var color = chart.data.datasets[i].backgroundColor;
                        var label = chart.data.datasets[i].label;
                        legendHtml.push(
                          '<div class="lgnd-item-container">'+
                            '<input class="lgnd-ckeckbox" type="checkbox" checked '+
                                    'onclick="updateDataset(event, '+datasetIndex+')"/>'+
                            '<div class="lgnd-square" style="background-color:'+color+'"></div>'+
                            '<div class="lgnd-label">'+label+' '+yAxis+'</div>'+
                            '<input class="lgnd-btn" type="button" value="Change" onclick="changeLeftRightY(event,'+datasetIndex +')"></input>'+
                          '</div>');
                    }
                }
                legendHtml.push('</div>');

                return legendHtml.join("");
            },
            legend:{
              display:false
            },
            customOffset: 2,
            responsive: true,
            tooltips: {
                mode: 'label',
                position: 'nearest'
            },
  scales: {
      xAxes: [{
        stacked: true
      }],
      yAxes: [{
        position: "left",
        id: "y-axis-0",
      }, {
        position: "right",
        id: "y-axis-1",
      }]
    }

}

// Show/hide chart by click legend
updateDataset = function (e, datasetIndex) {
            var index = datasetIndex;
            var ci = e.view.ch;
            var meta = ci.getDatasetMeta(index);
            // See controller.isDatasetVisible comment
            meta.hidden = meta.hidden === null ? !ci.data.datasets[index].hidden : null;
            // We hid a dataset ... rerender the chart
            ci.update();
        };

var ctx = document.getElementById("myChart");
// allocate and initialize a chart
window.ch = new Chart(ctx, {
  type: 'line',
  data: barChartData,
  options: options

});
document.getElementById("chartLegend").innerHTML = ch.generateLegend();

changeLeftRightY = function(e, index) {
            //console.log("-- " + data.datasets[index].yAxisID)
            var ci = e.view.ch;
            var axisIds = []
            var yAxes = ci.options.scales.yAxes;
            for (var i = 0; i < yAxes.length; i++) { // loop over the registered yAxes
                yAxes[i].gridLines.color = "rgba(0,0,0,0.1)"; // reset all scale gridlines to default
                axisIds.push(yAxes[i].id) // get all yAxes ids
            }
            var meta = ci.getDatasetMeta(index);
            var currentAxisId = meta.yAxisID
            var currentAxisIdIndex = axisIds.indexOf(currentAxisId);
            var newAxisIdIndex;
            // basically get the next axis id in the array, if the last one, get the first (circular)
            if (currentAxisIdIndex == axisIds.length - 1) newAxisIdIndex = 0;
            else newAxisIdIndex = currentAxisIdIndex + 1;

            var newAxisId = axisIds[newAxisIdIndex];
            meta.yAxisID = newAxisId // set the new axis id to the next one in the array

            axis = ci.scales[newAxisId];
            axis.options.gridLines.color = "rgba(0,0,0,1)"; // change lines color of the new axes
            document.getElementById('legend').innerHTML = ci.generateLegend();

            ci.update()
        }

CSS:

.myChartDiv {
  max-width: 600px;
  max-height: 400px;
}
.lgnd-square {
  float: left;
  height: 20px;
  width:20px;
  overflow:hidden; 
  margin: 5px;
}

.lgnd-ckeckbox{
  float:left;
  margin: 10px;
}
.lgnd-btn{
  float: left;
  margin: 5px;
}
.lgnd-item-container{
  width: 300px;
  height: 40px;
}
.lgnd-label{
  float:left;
  margin: 5px;
}

HTML:

<html>
  <body>
    <div class="myChartDiv">
      <canvas id="myChart" width="600" height="400"></canvas>
    </div>
    <div id="chartLegend"></div>
  </body>
</html>

Upvotes: 2

Related Questions