[Chartjs]-Chartjs Nested Pie/Doughnut charts

11👍

This is possible as of version 2.0 of Chart.js, I think. Here’s an example:

var ctx = document.getElementById("myChart");
var chartData = {
  labels: [
    "Red",
    "Blue",
    "Yellow"
  ],
  datasets: [{
    data: [300, 50, 100],
    backgroundColor: [
      "#FF6384",
      "#36A2EB",
      "#FFCE56"
    ],
    hoverBackgroundColor: [
      "#FF6384",
      "#36A2EB",
      "#FFCE56"
    ]
  }, {
    data: [200, 100, 25, 25, 66, 34],
    backgroundColor: [
      "#FF6384",
      "#36A2EB",
      "#FFCE56",
      "#FF6384",
      "#36A2EB",
      "#FFCE56"
    ],
    hoverBackgroundColor: [
      "#FF6384",
      "#36A2EB",
      "#FFCE56",
      "#FF6384",
      "#36A2EB",
      "#FFCE56"
    ]
  }]
};
var pieChart = new Chart(ctx, {
  type: 'pie',
  data: chartData
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.min.js"></script>

<canvas id="myChart" width="300" height="300"></canvas>

3👍

If you need a legend that contains all labels from the outer and inner dataset, you have to generate the legend labels yourself.

enter image description here

This is done by defining a legend.labels.generateLabels function together with a legend.onClick function that takes care of hiding and showing individual doughnut slices. This could look as follows:

legend: {
  position: 'left',
  labels: {
    generateLabels: () => {
      let labels = [];
      config.data.datasets.forEach((ds, iDs) => labels = labels.concat(ds.labels.map((l, iLabel) => ({
        datasetIndex: iDs,
        labelIndex: iLabel,
        text: l,
        fillStyle: ds.backgroundColor[iLabel],
        hidden: chart ? chart.getDatasetMeta(iDs).data[iLabel].hidden : false,
        strokeStyle: '#fff'
      }))));
      return labels;
    }
  },
  onClick: (event, legendItem) => {
    let metaData = chart.getDatasetMeta(legendItem.datasetIndex).data;
    metaData[legendItem.labelIndex].hidden = !metaData[legendItem.labelIndex].hidden;
    chart.update();
  }
},

Please take look at below runnable code and see how it works:

var outer_labels = ['A', 'B', 'C'];
var inner_labels = ['X', 'Y', 'Z'];

var config = {
  type: 'doughnut',
  data: {
    datasets: [{
      data: [40, 15, 45],
      backgroundColor: ['rgb(0, 140, 75)', 'rgb(94, 140, 0)', 'rgb(140, 89, 0)'],
      label: 'Outer Dataset',
      labels: outer_labels
    }, {
      data: [30, 20, 50],
      backgroundColor: ['rgb(247, 70, 74)', 'rgb(70, 191, 189)', 'rgb(253, 180, 91)'],
      label: 'Inner Dataset',
      labels: inner_labels
    }],
    labels: outer_labels.concat(inner_labels)
  },
  options: {
    rotation: -0.8 * Math.PI,
    legend: {
      position: 'left',
      labels: {
        generateLabels: () => {
          let labels = [];
          config.data.datasets.forEach((ds, iDs) => labels = labels.concat(ds.labels.map((l, iLabel) => ({
            datasetIndex: iDs,
            labelIndex: iLabel,
            text: l,
            fillStyle: ds.backgroundColor[iLabel],
            hidden: chart ? chart.getDatasetMeta(iDs).data[iLabel].hidden : false,
            strokeStyle: '#fff'
          }))));
          return labels;
        }
      },
      onClick: (event, legendItem) => {
        let metaData = chart.getDatasetMeta(legendItem.datasetIndex).data;
        metaData[legendItem.labelIndex].hidden = !metaData[legendItem.labelIndex].hidden;
        chart.update();
      }
    },
    tooltips: {
      callbacks: {
        label: (tooltipItem, data) => {
          let dataset = data.datasets[tooltipItem.datasetIndex];
          let index = tooltipItem.index;
          return dataset.labels[index] + ": " + dataset.data[index];
        }
      }
    }
  }
};

var chart = new Chart('canvas', config);
div {
  width: 300px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.js"></script>
<div>
  <canvas id="canvas"></canvas>
</div>

Leave a comment