Antony
Antony

Reputation: 4364

Two chartjs charts on page throwing data undefined error

I created the following question Horizontal overlapping of bars, not whole graph which was kindly answered (I was advised to create a new question!). Whilst the answer worked great - I have 2x charts on the same page which for some reason throws a Cannot read property 'data' of undefined error.

I don't want to include the entirity of my 2 charts but the afterUpdate code that moves the bars is as follows:

function(chart) {
    var datasets = chart.config.data.datasets;

    console.log(datasets);

    for (let iDs = 1; iDs < datasets.length; iDs++) {
        let dataset = datasets[iDs];

        // I added the following line however stacks the graph and doesn't display it correctly like the first
        if (typeof dataset._meta[0] !== 'undefined'){
            for (var i = 0; i < dataset._meta[0].data.length; i++) {
                let model = dataset._meta[0].data[i]._model;
                model.x += iDs * offset;
                model.controlPointNextX += iDs * offset;
                model.controlPointPreviousX += iDs * offset;
            }
        }
    }
}

I have added the typeof check to the code which at least renders the graph, however I would like both graphs to function the same way whereas the addition of the typeof check then just stacks the graph and ignores the code to move the bars.

This can best be seen in action in the following fiddle: https://jsfiddle.net/ycpj7g8w/

Upvotes: 0

Views: 868

Answers (1)

uminder
uminder

Reputation: 26190

The described behavior is weird, given that both charts work fine if they're created isolated from each other. The problem can be solved by changing the afterUpdate function and make use of the chart's .getDatasetMeta(index) function to obtain the dataset's metadata.

afterUpdate: function(chart) {
  let datasets = chart.config.data.datasets;
  for (let iDs = 1; iDs < datasets.length; iDs++) {
    let meta = chart.getDatasetMeta(iDs);
    for (var i = 0; i < meta.data.length; i++) {
      let model = meta.data[i]._model;
      model.x += iDs * offset;
      model.controlPointNextX += iDs * offset;
      model.controlPointPreviousX += iDs * offset;
    }
  }
}

Please take a look at your amended and runnable code below:

Chart.defaults.global.legend.labels.usePointStyle = true;
Chart.defaults.scale.gridLines.drawOnChartArea = false;

var offset = 6;

new Chart('graph_1', {
  "type": "bar",
  "data": {
    "labels": ["2020-08-01", "2020-09-01", "2020-10-01"],
    "datasets": [{
        "label": "Title test 1",
        "data": [30, 20, 60],
        "xAxisID": "bar-x-axis-1",
        "barThickness": 14,
        "backgroundColor": "rgba(241,213,157,1)",
        "borderColor": "rgba(241,213,157,1)",
        "pointBackgroundColor": "rgba(241,213,157,1)"
      },
      {
        "label": "Title test 2",
        "data": [30, 20, 60],
        "xAxisID": "bar-x-axis-2",
        "barThickness": 14,
        "backgroundColor": "rgba(237,141,120,1)",
        "borderColor": "rgba(237,141,120,1)",
        "pointBackgroundColor": "rgba(237,141,120,1)"
      },
      {
        "label": "Title test 3",
        "data": [30, 20, 60],
        "xAxisID": "bar-x-axis-3",
        "barThickness": 14,
        "backgroundColor": "rgba(120,199,212,1)",
        "borderColor": "rgba(120,199,212,1)",
        "pointBackgroundColor": "rgba(120,199,212,1)"
      }
    ]
  },
  "plugins": [{
    "afterUpdate": function(chart) {
      let datasets = chart.config.data.datasets;
      for (let iDs = 1; iDs < datasets.length; iDs++) {
        let meta = chart.getDatasetMeta(iDs);
        for (var i = 0; i < meta.data.length; i++) {
          let model = meta.data[i]._model;
          model.x += iDs * offset;
          model.controlPointNextX += iDs * offset;
          model.controlPointPreviousX += iDs * offset;
        }
      }
    }
  }],
  "curvature": 0.2,
  "options": {
    "legend": {
      "display": true,
      "position": "bottom"
    },
    "scales": {
      "yAxes": [{
        "ticks": {
          "beginAtZero": true,
          "stepSize": 25
        }
      }],
      "xAxes": [{
          "id": "bar-x-axis-3",
          "display": false
        },
        {
          "id": "bar-x-axis-2",
          "display": false,
          "offset": true
        },
        {
          "id": "bar-x-axis-1",
          "display": false,
          "offset": true
        }
      ]
    },
    "barRoundness": 0.5
  }
});

new Chart('graph_2', {
  "type": "bar",
  "data": {
    "labels": ["2020-06-01", "2020-07-01", "2020-08-01", "2020-09-01", "2020-10-01"],
    "datasets": [{
        "label": "Stage 1",
        "data": [30, 75, 60, 90],
        "xAxisID": "bar-x-axis-1",
        "barThickness": 14,
        "backgroundColor": "rgba(241,213,157,1)",
        "borderColor": "rgba(241,213,157,1)",
        "pointBackgroundColor": "rgba(241,213,157,1)"
      },
      {
        "label": "Stage 2",
        "data": [40, 66, 60, 90],
        "xAxisID": "bar-x-axis-2",
        "barThickness": 14,
        "backgroundColor": "rgba(237,141,120,1)",
        "borderColor": "rgba(237,141,120,1)",
        "pointBackgroundColor": "rgba(237,141,120,1)"
      },
      {
        "label": "Stage 3",
        "data": [50, 45, 60, 90],
        "xAxisID": "bar-x-axis-3",
        "barThickness": 14,
        "backgroundColor": "rgba(120,199,212,1)",
        "borderColor": "rgba(120,199,212,1)",
        "pointBackgroundColor": "rgba(120,199,212,1)"
      },
      {
        "label": "Stage 4",
        "data": [60, 35, 60, 90],
        "xAxisID": "bar-x-axis-4",
        "barThickness": 14,
        "backgroundColor": "rgba(5,114,159,1)",
        "borderColor": "rgba(5,114,159,1)",
        "pointBackgroundColor": "rgba(5,114,159,1)"
      },
      {
        "label": "Stage 5",
        "data": [70, 25, 60, 90],
        "xAxisID": "bar-x-axis-5",
        "barThickness": 14,
        "backgroundColor": "rgba(2,41,112,1)",
        "borderColor": "rgba(2,41,112,1)",
        "pointBackgroundColor": "rgba(2,41,112,1)"
      }
    ]
  },
  "plugins": [{
    "afterUpdate": function(chart) {
      let datasets = chart.config.data.datasets;
      for (let iDs = 1; iDs < datasets.length; iDs++) {
        let meta = chart.getDatasetMeta(iDs);
        for (var i = 0; i < meta.data.length; i++) {
          let model = meta.data[i]._model;
          model.x += iDs * offset;
          model.controlPointNextX += iDs * offset;
          model.controlPointPreviousX += iDs * offset;
        }
      }
    }
  }],
  "curvature": 0.2,
  "options": {
    "legend": {
      "display": true,
      "position": "bottom"
    },
    "scales": {
      "yAxes": [{
        "ticks": {
          "beginAtZero": true,
          "stepSize": 25
        }
      }],
      "xAxes": [{
          "id": "bar-x-axis-5",
          "display": false,
        },
        {
          "id": "bar-x-axis-4",
          "display": false,
          "offset": true
        },
        {
          "id": "bar-x-axis-3",
          "display": false,
          "offset": true
        },
        {
          "id": "bar-x-axis-2",
          "display": false,
          "offset": true
        },
        {
          "id": "bar-x-axis-1",
          "display": false,
          "offset": true
        }
      ]
    },
    "barRoundness": 0.5
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="graph_1"></canvas>
<canvas id="graph_2"></canvas>

Please note that when all charts contained on a page use the same plugin function, you don't have to repeat the code but can simply register it once through Chart.pluginService.register as shown in this answer.

Upvotes: 2

Related Questions