Search code examples
typescriptchart.jsreact-chartjs

Canvas: "object is undefined"


I'm trying to create a line in Canvas. Everything was going well until I got stuck on this part:

import { Doughnut } from "react-chartjs-2";
import "chart.js/auto";
import { Chart, ChartData, ChartDataset, ChartOptions } from "chart.js";

function DoughnutChart(){
  const doughnutLabelsLine = {
    id: "doughnutLabelsLine",
    afterDraw(chart: Chart<"doughnut">, args: any, options: ChartOptions) {
      const {
        ctx,
        chartArea: { top, bottom, left, right, width, height },
      } = chart;

      chart.data.datasets.forEach((dataset, i) => {
        // console.log(chart.getDatasetMeta(i));
        chart.getDatasetMeta(i).data.forEach((datapoint, index) => {
          console.log(dataset);
          const { x, y } = datapoint.tooltipPosition();
          //Object is possibly undefined
          ctx.fillStyle = dataset?.borderColor[index];
          ctx.fillRect(x, y, 2, 2);
        });
      });
    },
  };

//config...
//dataDoughnut

return (
        <div className="col-span-6">
          <Doughnut
            data={dataDoughnut}
            options={config}
            width={200}
            height={300}
            plugins={[doughnutLabelsLine]}
          />
        </div>
);
}
export default DoughnutChart;

I have tried putting it into an if statement as well and using an exclamation mark, but that didn't work. Am I missing something here?

Edit: Made an adjustment based on user2057925's suggestion

DoughnutLabelsLine

  const doughnutLabelsLine = {
    id: "doughnutLabelsLine",
    afterDraw(chart: Chart<"doughnut">, args: any, options: ChartOptions) {
      const {
        ctx,
        chartArea: { top, bottom, left, right, width, height },
      } = chart;

      chart.data.datasets.forEach((dataset, i) => {
        // console.log(chart.getDatasetMeta(i));
        chart.getDatasetMeta(i).data.forEach((datapoint, index) => {
          console.log(dataset);
          const { x, y } = datapoint.tooltipPosition();
          ctx.save();
          const borderColor = datapoint.options.borderColor as
            | string
            | CanvasGradient
            | CanvasPattern;
          ctx.fillStyle = borderColor;
          // ctx.fillStyle = dataset?.borderColor[index];
          ctx.fillRect(x, y, 10, 10);
          ctx.restore();
        });
      });
    },
  };

Solution

  • I think the undefined error is not related to the dataset instance but due to the borderColor one.

    Anyway the best thing could be to use the datapoint.options.borderColor because:

    1. you shouldn't need either the dataset instance or the index
    2. the datapoint, being an ArcElement, provides in options node all its options, even if they are configured (using default).

    const p = {
        id: "doughnutLabelsLine",
        afterDraw(chart, args, options) {
          chart.data.datasets.forEach((dataset, i) => {
            chart.getDatasetMeta(i).data.forEach((datapoint, index) => {
              const { x, y } = datapoint.tooltipPosition();
              ctx.save();
              ctx.fillStyle = datapoint.options.borderColor;
              ctx.fillRect(x -5, y - 5, 10, 10);
              ctx.restore();
            });
          });
        }
    };
    
    const config = {
        type: 'doughnut',
        plugins: [p],  
        data: {
          labels: [6, 6, 4, 3, 2, 2, 1],
          datasets: [{
            label: 'Simple treemap',
            data: [6, 6, 4, 3, 2, 2, 1],
            borderColor: ['red', 'blue']
          }]
        },
      };
    const ctx = document.getElementById('myChart').getContext('2d');
    new Chart(ctx, config);
    .myChartDiv {
      max-width: 600px;
      max-height: 400px;
    }
    <html>
      <head>
        <script src="https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js"></script>
      </head> 
      <body>
        <div class="myChartDiv">
          <canvas id="myChart" width="600" height="400"/>
        </div>
      </body>
    </html>