Using the dataURI method, I am not getting any color for the series of my pie chart - apexcharts

I have the following code running:
var options = {
chart: {
type: 'donut',
fontFamily: 'Lato Light'
},
series: [1,2,3,4,5],
labels: ['1','2','3','4','5'],
theme: {
monochrome: {
enabled: true,
color: '#b19254',
shadeTo: 'dark',
shareIntensity: 0.15
}
},
//colors: ['#b19254', '#9f834c', '#8e7543', '#7c663b', '#b99d65', '#c8b387'],
legend: {
position: 'bottom'
},
plotOptions: {
pie: {
donut: {
labels: {
show: true,
name: {
show: false
},
value: {
offsetY: -1,
show: true
},
total: {
show: false,
showAlways: false,
formatter: function (w) { return String(Math.round(chart.w.globals.seriesTotals.reduce((a,b) => { return a+b}, 0) * 100) / 100) + ' ' + $currency}
}
}
}
}
},
}
var chart = new ApexCharts(document.querySelector("#investment-chart-wrapper"), options);
chart.render();
var $chartData = chart.dataURI();
$chartData.then(
(result) => {
document.querySelector('#chartimg').setAttribute('src',result.imgURI);
});
The bit I am fighting with is the promise result of the dataURI() method from here.
For some reason, the chart I get has all the information including the series labels, but the color for the series does not show, leaving me with this. The color is used for the legend at the bottom, however.
I am sure I am missing something here. Please let me know what.

I was running into this problem as well today. It was because the animation of the chart has not taken place yet. You have to get the dataURI() after it has fully rendered or turn off the chart animation.
I was able to get this working by setting the rendered chart to a variable at the top of my js file and then using it in a function like this:
function SetChartImage() {
chartHistoricalPCTArea.dataURI().then(({ imgURI }) => {
var image = document.querySelector('#HistoricalPCTImage');
image.src = imgURI;
})
}

Related

Vue 3 & Chart JS not updating labels

I'm trying to make a simple Vue3 app which show graphs using Chart.js
For this I'm trying to replicate the code shown in the vue-chart-3 plugin doc, which shows an example using a Doughnut chart
My objective is to show a Line graph with a horizontal time axis
The code is a simple App.vue which template is
<template>
<LineChart v-bind="lineChartProps" />
</template>
And the script part:
<script lang="ts">
import { computed, defineComponent, ref } from "vue";
import { LineChart, useLineChart } from "vue-chart-3";
import { Chart, ChartData, ChartOptions, registerables } from "chart.js";
Chart.register(...registerables);
export default defineComponent({
name: "App",
components: { LineChart },
setup() {
const dataValues = ref([30, 40, 60, 70, 5]);
const dataLabels = ref(["Paris", "Nîmes", "Toulon", "Perpignan", "Autre"]);
const toggleLegend = ref(true);
const testData = computed<ChartData<"doughnut">>(() => ({
labels: dataLabels.value,
datasets: [
{
data: dataValues.value,
backgroundColor: [
"#77CEFF",
"#0079AF",
"#123E6B",
"#97B0C4",
"#A5C8ED",
],
},
],
}));
const options = computed<ChartOptions<"doughnut">>(() => ({
scales: {
myScale: {
type: "logarithmic",
position: toggleLegend.value ? "left" : "right",
},
},
plugins: {
legend: {
position: toggleLegend.value ? "top" : "bottom",
},
title: {
display: true,
text: "Chart.js Doughnut Chart",
},
},
}));
const { lineChartProps, lineChartRef } = useLineChart({
chartData: testData,
options,
});
function switchLegend() {
toggleLegend.value = !toggleLegend.value;
}
return {
switchLegend,
testData,
options,
lineChartRef,
lineChartProps,
};
},
mounted() {
if (localStorage.data == undefined) {
localStorage.data = JSON.stringify(this.data);
} else {
this.data = localStorage.data;
}
let dataProcessed = JSON.parse(this.data);
// console.log(JSON.parse(this.data));
console.log(dataProcessed.data);
var dates = [];
// Obtain dates from dataProcessed Array
for (var i = 0; i < dataProcessed.data.length; i++) {
dates.push(dataProcessed.data[i].date);
}
this.testData.labes = dates;
console.log(dates);
},
});
</script>
The objective is that the mounted hook gets certain parameters of the LocalStorage and put them in the "labels" array of the "testData" variable, which is the one which aparently stores the X axis data of the chart.
In the VUE developer tool, it can be seen how this assignation process is done correctly, but in the chart of the left side, the data have not been updated.
Thank you for your help :D

How to dynamically update Chartjs legend label colors?

I am using vue-chartjs and chartjs-plugin-colorschemes to style a doughnut graph. I'm trying to allow the user to choose from a select which theme they prefer. I have it 90% working; the user can select a theme, hit update, and the doughnut plus its label correctly change color. What doesn't work though, is on initial page load, the doughnut has a color scheme but the legend does not.
I am currently passing a default theme down as props, and I am using a watch method to watch for changes to the theme. The error occurs inside of this watch method.
How can I dynamically update the legend label colors? Here is a minimal example of my component:
<script>
/* eslint-disable no-new */
import convert from 'convert-units';
import { Doughnut } from 'vue-chartjs';
import Chart from 'chart.js';
import { calculateCategoryWeight } from '~/helpers/functions';
import 'chartjs-plugin-colorschemes';
export default {
extends: Doughnut,
props: {
selected: {
type: Object,
default: () => {}
},
theme: {
type: String,
default: ''
}
},
data () {
const vm = this;
return {
chartData: {
labels: this.selected.categories.map(category => {
return this.$options.filters.truncate(category.name, 20);
}),
datasets: [
{
label: 'Selected Graph',
data: this.selected.categories.map(category => {
return parseFloat(convert(category).from('g').to('oz')).toFixed(2);
})
}
]
},
options: {
cutoutPercentage: 75,
legend: {
display: true,
position: 'right'
},
plugins: {
colorschemes: {
scheme: this.theme
}
},
responsive: true,
maintainAspectRatio: false,
tooltips: {
enabled: false
},
}
};
},
mounted () {
this.renderChart(this.chartData, this.options);
},
watch: {
theme (newVal, oldVal) {
const { chart } = this.$data._chart;
chart.options.plugins.colorschemes.scheme = newVal; //<--- updates chart only
chart.update();
}
}
};
</script>
Well I discovered the fix finally.
Essentially in the watch method, I was digging in too deep into the chart instance. By moving up a level in the chart object, both the legend and chart colors are both updated correctly.
watch: {
theme (newVal, oldVal) {
const chart = this.$data._chart; //<-- changed here
chart.options.plugins.colorschemes.scheme = newVal;
chart.update();
}
}

ChartJS tooltip label for pie cart being cut

I am trying to add tooltip in my chart, however some of them the label are being cut.
Hier is my code
var myPieChart = new Chart(ctx, {
type: 'pie',
data: data,
options: {
title: {
display: true,
text: title,
fontSize: 36
},
tooltips: {
bodyFontSize: 22,
callbacks: {
label: function (tooltipItem, data) {
var number = data.datasets[0].data[tooltipItem.index];
var label = data.labels[tooltipItem.index];
var sum = data.datasets[0].data.reduce(function (a, b) { return a + b; }, 0);
var percentage = getPercent(number, sum);
return label + " " + Math.round(percentage) + " %";
}
}
}
}
Can anyone help me to make the label rendered properly? Many thanks
Regards
I would say your tooltip is going off the edge of your canvas, have a look at this jsfiddle for an example. Or see sample code at the end.
Or, without seeing the full code, I might assume that your 2014 chart is overlapping the 2015 chart. Maybe fixed with a z-index in CSS, but then the opposite effect with the other chart may happen?
Same code from jsfiddle:
<div style='width: 300px;'>
<canvas id='chart'></canvas>
</div>
var ctx = document.getElementById('chart');
var chart = new Chart(ctx, {
type: 'pie',
data: {
labels: ["I am a really really really really really really really really really really really reallyreally really really really really really long label so long I might go off the chart"],
datasets: [{
data: [5],
}]
},
options: {
legend: {
display: false
},
maintainAspectRatio: false,
title: {
display: false,
fontSize: 16
}
}
});

Chart.js legend customisation

I have a bit of a strange config of my chart.js because of the data that is being fed into it and also the line colours. However I was wondering if somebody could point me in the direction of how to customise the legend:
$(document).ready(function(){
$.ajax({
url : "../acredash/teamData.php",
timeout: 4000,
type : "GET",
success :function(data){
console.log(data);
var chartata = {
labels: [
"Strategic Development and Ownership",
"Driving change through others",
"Exec Disposition",
"Commercial Acumen",
"Develops High Performance Teams",
"Innovation and risk taking",
"Global Leadership",
"Industry Leader"
]};
var ctx = $("#mycanvas");
var config = {
type: 'radar',
data: chartata,
animationEasing: 'linear',
options: {
legend: {
display: true,
position: 'bottom'
},
tooltips: {
enabled: true
},
scale: {
ticks: {
fontSize: 15,
beginAtZero: true,
stepSize: 1
}
}
},
},
LineGraph = new Chart(ctx, config);
var colorArray = [
["#7149a5", false],
["#57B6DD", false],
["#36bfbf", false],
["#69bd45", false],
['#9adfdf', false],
['#c6b6db' ,false],
["#5481B1", false],
['#8d6db7', false],
['#d2ebc7', false],
["#6168AC", false]
];
for (var i in data) {
tmpscore=[];
tmpscore.push(data[i].score_1);
tmpscore.push(data[i].score_2);
tmpscore.push(data[i].score_3);
tmpscore.push(data[i].score_4);
tmpscore.push(data[i].score_5);
tmpscore.push(data[i].score_6);
tmpscore.push(data[i].score_7);
tmpscore.push(data[i].score_8);
var color, done = false;
while (!done) {
var test = colorArray[parseInt(Math.random() * 10)];
if (!test[1]) {
color = test[0];
colorArray[colorArray.indexOf(test)][1] = true;
done = !done;
}
}
newDataset = {
label: data[i].firstName+' '+data[i].lastName,
borderColor: color,
backgroundColor: "rgba(0,0,0,0)",
data: tmpscore,
};
config.data.datasets.push(newDataset);
}
LineGraph.update();
},
});
});
I have looked around without much luck because of how my chart is being generated. It just used the default legend and its a it messy. I would like to just have control over it.
Step 1:
set callback for legend on options
legendCallback: function(chart) {
var legendHtml = [];
legendHtml.push('<ul>');
var item = chart.data.datasets[0];
for (var i=0; i < item.data.length; i++) {
legendHtml.push('<li>');
legendHtml.push('<span></span>');//add what ever you want :-p
legendHtml.push('<span class="chart-legend-label-text">' + chart.data.labels[i]+'</span>');
legendHtml.push('</li>');
}
legendHtml.push('</ul>');
return legendHtml.join("");
},legend:false,
Step 2:
Place your legend where ever you want :-p
<div id="my-legend-con" class="legend-con"></div>
Step 3:
Initialise the legend.
$('#my-legend-con').html(myChartName.generateLegend());
Step 3:
Do what ever you want...

Pie Chart Legend - Chart.js

I need help to put the number of the pie chart in the legend
Chart Image
If i hover the chart with mouse i can see the number relative to each item
i want to display it in the legend either
the important code so far:
var tempData = {
labels: Status,
datasets: [
{
label: "Status",
data: Qtd,
backgroundColor: randColor
},
]
};
var ctx = $("#pieStatus").get(0).getContext("2d");
var chartInstance = new Chart(ctx, {
type: 'pie',
data: tempData,
options: {
title: {
display: true,
fontsize: 14,
text: 'Total de Pedidos por Situação'
},
legend: {
display: true,
position: 'bottom',
},
responsive: false
}
});
"Qtd","randColor" are "var" already with values
You need to edit the generateLabels property in your options :
options: {
legend: {
labels: {
generateLabels: function(chart) {
// Here
}
}
}
}
Since it is quite a mess to create on your own a great template. I suggest using the same function as in the source code and then edit what is needed.
Here are a small jsFiddle, where you can see how it works (edited lines - from 38 - are commented), and its result :
Maybe this is a hacky solution, but for me seems simpler.
The filter parameter
ChartJS legend options have a filter parameter. This is a function that is called for each legend item, and that returns true/false whether you want to show this item in the legend or not.
filter has 2 arguments:
legendItem : The legend item to show/omit. Its properties are described here
data : The data object passed to the chart.
The hack
Since JS passes objects by reference, and filter is called for each legend item, then you can mutate the legendItem object to show the text that you want.
legend : {
labels: {
filter: (legendItem, data) => {
// First, retrieve the data corresponding to that label
const label = legendItem.text
const labelIndex = _.findIndex(data.labels, (labelName) => labelName === label) // I'm using lodash here
const qtd = data.datasets[0].data[labelIndex]
// Second, mutate the legendItem to include the new text
legendItem.text = `${legendItem.text} : ${qtd}`
// Third, the filter method expects a bool, so return true to show the modified legendItem in the legend
return true
}
}
}
Following on from tektiv's answer, I've modified it for ES6 which my linter requires;
options: {
legend: {
labels: {
generateLabels: (chart) => {
const { data } = chart;
if (data.labels.length && data.datasets.length) {
return data.labels.map((label, i) => {
const meta = chart.getDatasetMeta(0);
const ds = data.datasets[0];
const arc = meta.data[i];
const custom = (arc && arc.custom) || {};
const { getValueAtIndexOrDefault } = Chart.helpers;
const arcOpts = chart.options.elements.arc;
const fill = custom.backgroundColor ? custom.backgroundColor : getValueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor);
const stroke = custom.borderColor ? custom.borderColor : getValueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor);
const bw = custom.borderWidth ? custom.borderWidth : getValueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth);
const value = chart.config.data.datasets[arc._datasetIndex].data[arc._index];
return {
text: `${label}: ${value}`,
fillStyle: fill,
strokeStyle: stroke,
lineWidth: bw,
hidden: Number.isNaN(ds.data[i]) || meta.data[i].hidden,
index: i,
};
});
}
return [];
},
},
},
},
I wanted to let the user select from 100+ data sets, but rather than adding/removing them from my Chart I decided to set the showLine: false on any dataset that I want hidden. Unfortunately the default legend would show all 100+. So in my solution I generate the legend manually, filtering out any dataset that has showLine: false.
Your settings will have this:
legend: {
labels: {
generateLabels: (a) => {
return a.data.labels
}
}
And you'll generate your own labels with a helper function:
function updateAllLabels() {
const myNewLabels = [];
myChart.data.datasets.forEach((element) => {
if (element.showLine) {
myNewLabels.push(generateLabel(element));
}
});
myChart.data.labels = myNewLabels;
}
And you'll generate the label with another function:
function generateLabel(data) {
return {
fillStyle: data.borderColor,
lineWidth: 1,
strokeStyle: data.borderColor,
text: data.countyName, // I attach countryName to my datasets for convenience
}
}
Now just don't forget to call the function whenever updating your chart:
updateAllLabels();
myChart.update();
Happy graphing!