Kieran McClung
Kieran McClung

Reputation: 724

Conditional in ChartJS axes tick callback function isn't returning the expected labels

I have a chart containing data for each day of the year and I'm wanting to show the x-axis simply as months.

I've set up the following callback function which (crudely) grabs the month from the set of labels, checks to see whether it already exists and if not, returns it as an axis label

let rollingLabel;
...
function(label, index, labels) {
    let _label = label.replace(/[0-9]/g, '');
              
    if (rollingLabel != _label) {
        rollingLabel = _label;
        return rollingLabel;
    }
}

However, it's only returning two of the expected four labels.

What's confusing me more is that if I add console.log(rollingLabel) within the conditional I can see that the variable is updating how I'd expect but it's not returning the value, or it is and the chart isn't picking it up for whatever reason. Even more confusing is that if I uncomment line 48 // return _label the chart updates with all the labels so I don't believe it's an issue with max/min settings for the chart.

If anyone has any ideas I'd be most grateful. I've been staring at it for hours now!

The expected output for the below snippet should have the following x-axis labels:

Aug | Sep | Oct | Nov

const canvas = document.getElementById('chart');
const ctx    = canvas.getContext('2d');

let data     = [
  1,6,3,11,5,1,2,6,2,10,5,8,1,1,2,4,5,2,3,1
];

let labels = [
  "Aug 1","Aug 2","Aug 3","Aug 4","Aug 5","Sep 1","Sep 2","Sep 3","Sep 4","Sep 5","Oct 1","Oct 2","Oct 3","Oct 4","Oct 5","Nov 1","Nov 2", "Nov 3","Nov 4","Nov 5"
];

let rollingLabel;

chart = new Chart(ctx, {
  type: "line",
  data: {
    datasets: [
      {
        backgroundColor: '#12263A',
        data: data,
        pointRadius: 0
      }
    ],
    labels: labels,
  },
  options: {
    legend: {
      display: false
    },
    responsive: false,
    scales: {
      xAxes: [
        {
          gridLines: {
            display: false
          },
          ticks: {
            display: true,
            autoSkip: true,
            callback: function(label, index, labels) {
              let _label = label.replace(/[0-9]/g, '');
              
              if (rollingLabel != _label) {
                rollingLabel = _label;
                return rollingLabel;
              }
              
              // return _label;
            }
          }
        }
      ]
    },
    tooltips: {
      mode: "index",
      intersect: false
    },
    hover: {
      mode: "index",
      intersect: false
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="chart"></canvas>

Upvotes: 2

Views: 15783

Answers (3)

Sanlyn
Sanlyn

Reputation: 169

I found a easy solution via the chart.js documentation.

const config = {
  type: 'line',
  data: data,
  options: {
    responsive: true,
    plugins: {
      title: {
        display: true,
        text: 'Chart with Tick Configuration'
      }
    },
    scales: {
      x: {
        ticks: {
          // For a category axis, the val is the index so the lookup via getLabelForValue is needed
          callback: function(val, index) {
            // Hide the label of every 2nd dataset
            return index % 2 === 0 ? this.getLabelForValue(val) : '';
          },
          color: 'red',
        }
      }
    }
  },
};

The callback function decides what labels will be shown. Current setup shows every 2nd label, if you want to show every 3rd for example you would change:

return index % 2 === 0 ? this.getLabelForValue(val) : '';

to:

return index % 3 === 0 ? this.getLabelForValue(val) : '';

Upvotes: 8

Acc
Acc

Reputation: 21

The chartjs documentation is not so accurate. If you set the label of the tick which should be skipped to be '', it won't be skipped, it will show a blank label. Instead, return null or do not return anything, as in uminder's answer.

Upvotes: 0

uminder
uminder

Reputation: 26150

You need to define ticks.autoSkip: false on the x-axis to make it work as expected:

autoSkip: If true, automatically calculates how many labels can be shown and hides labels accordingly. Labels will be rotated up to maxRotation before skipping any. Turn autoSkip off to show all labels no matter what.

Please take a look at your amended code below:

let data     = [
  1,6,3,11,5,1,2,6,2,10,5,8,1,1,2,4,5,2,3,1
];

let labels = [
  "Aug 1","Aug 2","Aug 3","Aug 4","Aug 5","Sep 1","Sep 2","Sep 3","Sep 4","Sep 5","Oct 1","Oct 2","Oct 3","Oct 4","Oct 5","Nov 1","Nov 2", "Nov 3","Nov 4","Nov 5"
];

let rollingLabel;

chart = new Chart('chart', {
  type: "line",
  data: {
    datasets: [
      {
        backgroundColor: '#12263A',
        data: data,
        pointRadius: 0
      }
    ],
    labels: labels,
  },
  options: {
    legend: {
      display: false
    },
    responsive: false,
    scales: {
      xAxes: [
        {
          gridLines: {
            display: false
          },
          ticks: {
            display: true,
            autoSkip: false,
            callback: function(label, index, labels) {
              let _label = label.replace(/[0-9]/g, ''); 
              if (rollingLabel != _label) {
                rollingLabel = _label;
                return rollingLabel;
              }
            }
          }
        }
      ]
    },
    tooltips: {
      mode: "index",
      intersect: false
    },
    hover: {
      mode: "index",
      intersect: false
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="chart"></canvas>

Upvotes: 7

Related Questions