4
votes

I need to create custom legend for my donut chart using ChartJS library. I have created donut with default legend provided by ChartJS but I need some modification.

I would like to have value above the car name. Also I don't like sticky legend I want to have it separate from donut so I can change the style for fonts, boxes (next to the text "Audi" for example)

I know there is some Legend generator but I'm not sure how to use it with VueJS - because I'm using VueJS as a framework

This is how my legend looks like now - http://imgur.com/a/NPUoi

My code:

From Vue component where I import a donut component:

<div class="col-md-6">
  <div class="chart-box">
    <p class="chart-title">Cars</p>
    <donut-message id="chart-parent"></donut-message>
  </div>
</div>

Javascript:

    import { Doughnut } from 'vue-chartjs'
    export default Doughnut.extend({

    ready () {


    Chart.defaults.global.tooltips.enabled = false;
    Chart.defaults.global.legend.display = false;

    this.render({
      labels: ['Audi','BMW','Ford','Opel'],
      datasets: [
        {
          label: 'Cars',
          backgroundColor: ['#35d89b','#4676ea','#fba545','#e6ebfd'],
          data: [40, 30, 20, 10]
        }
      ]
    }, 
    {
      responsive: true,
      cutoutPercentage: 75,
      legend: {
        display: true,
        position: "right",
        fullWidth: true,
        labels: {
          boxWidth: 10,
          fontSize: 14
        }
      },
      animation: {
        animateScale: true
      }
    })
  }
});
3
Vue-ChartJS - but in base its ChartJSDenis Lapadatovic

3 Answers

9
votes

I'm having the same problem trying to understand the documentation and this link might clarify the process of customize the legends:

https://codepen.io/michiel-nuovo/pen/RRaRRv

The trick is to track a callback to build your own HTML structure and return this new structure to ChartJS.

Inside the options object:

legendCallback: function(chart) {
  var text = [];
  text.push('<ul class="' + chart.id + '-legend">');
  for (var i = 0; i < chart.data.datasets[0].data.length; i++) {
    text.push('<li><span style="background-color:' + 
    chart.data.datasets[0].backgroundColor[i] + '">');
      if (chart.data.labels[i]) {
      text.push(chart.data.labels[i]);
    }
    text.push('</span></li>');
  }
  text.push('</ul>');
  return text.join("");
}

Second, you need a container to insert the new html and using the method myChart.generateLegend() to get the customized html:

$("#your-legend-container").html(myChart.generateLegend());

After that, if you need, track down the events:

$("#your-legend-container").on('click', "li", function() {
  myChart.data.datasets[0].data[$(this).index()] += 50;
  myChart.update();
  console.log('legend: ' + data.datasets[0].data[$(this).index()]);
});

$('#myChart').on('click', function(evt) {
  var activePoints = myChart.getElementsAtEvent(evt);
  var firstPoint = activePoints[0];
  if (firstPoint !== undefined) {
    console.log('canvas: ' + 
    data.datasets[firstPoint._datasetIndex].data[firstPoint._index]);
  } 
  else {
    myChart.data.labels.push("New");
    myChart.data.datasets[0].data.push(100);
    myChart.data.datasets[0].backgroundColor.push("red");
    myChart.options.animation.animateRotate = false;
    myChart.options.animation.animateScale = false;
    myChart.update();
    $("#your-legend-container").html(myChart.generateLegend());
  }
}

Another solution that I found, if you don't need to change the HTMl structure inside the legend, you can just insert the same HTML in your legend container and customize it by CSS, check this another link:

http://jsfiddle.net/vrwjfg9z/

Hope it works for you.

0
votes

You can extract the legend markup.

data () {
  return {
     legendMarkup: ''
  }
},
ready () {
  this.legendMarkup = this._chart.generateLegend()
 }

And in your template you can output it.

<div class="legend" ref="legend" v-html="legendMarkup"></div>

this._chart is the internal chartjs instance in vue-chartjs. So you can call all chartjs methods which are not exposed by vue-chartjs api over it.

However you can also use the legend generator. The usage is the same in vue. You can pass in the options, use callbacks etc.

-5
votes

please check this documentation .

Legend Configuration

The chart legend displays data about the datasets that area appearing on the chart.

Configuration options Position of the legend. Options are:

'top' 'left' 'bottom' 'right'

Legend Item Interface

Items passed to the legend onClick function are the ones returned from labels.generateLabels. These items must implement the following interface.

{
    // Label that will be displayed
    text: String,

    // Fill style of the legend box
    fillStyle: Color,

    // If true, this item represents a hidden dataset. Label will be rendered with a strike-through effect
    hidden: Boolean,

    // For box border. See https://developer.mozilla.org/en/docs/Web/API/CanvasRenderingContext2D/lineCap
    lineCap: String,

    // For box border. See https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setLineDash
    lineDash: Array[Number],

    // For box border. See https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineDashOffset
    lineDashOffset: Number,

    // For box border. See https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineJoin
    lineJoin: String,

    // Width of box border
    lineWidth: Number,

    // Stroke style of the legend box
    strokeStyle: Color

    // Point style of the legend box (only used if usePointStyle is true)
    pointStyle: String
}

Example

The following example will create a chart with the legend enabled and turn all of the text red in color.

var chart = new Chart(ctx, { type: 'bar', data: data, options: { legend: { display: true, labels: { fontColor: 'rgb(255, 99, 132)' } } } }); Custom On Click Actions

It can be common to want to trigger different behaviour when clicking an item in the legend. This can be easily achieved using a callback in the config object.

The default legend click handler is:

function(e, legendItem) {
    var index = legendItem.datasetIndex;
    var ci = this.chart;
    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();
}
Lets say we wanted instead to link the display of the first two datasets. We could change the click handler accordingly.

var defaultLegendClickHandler = Chart.defaults.global.legend.onClick;
var newLegendClickHandler = function (e, legendItem) {
    var index = legendItem.datasetIndex;

    if (index > 1) {
        // Do the original logic
        defaultLegendClickHandler(e, legendItem);
    } else {
        let ci = this.chart;
        [ci.getDatasetMeta(0),
         ci.getDatasetMeta(1)].forEach(function(meta) {
            meta.hidden = meta.hidden === null? !ci.data.datasets[index].hidden : null;
        });
        ci.update();
    }
};

var chart = new Chart(ctx, {
    type: 'line',
    data: data,
    options: {
        legend: {

        }
    }
});

Now when you click the legend in this chart, the visibility of the first two datasets will be linked together.

HTML Legends

Sometimes you need a very complex legend. In these cases, it makes sense to generate an HTML legend. Charts provide a generateLegend() method on their prototype that returns an HTML string for the legend.

To configure how this legend is generated, you can change the legendCallback config property.

var chart = new Chart(ctx, {
    type: 'line',
    data: data,
    options: {
        legendCallback: function(chart) {
            // Return the HTML string here.
        }
    }
});