Adding color dynamically to Chart.js - javascript

I have a Chart.js project, best explained by this code below.
const response = [
{
"mmyy":"2019-12",
"promocode":"promo1",
"amount":"2776"
},
{
"mmyy":"2020-01",
"promocode":"promo1",
"amount":"1245"
},
{
"mmyy":"2020-01",
"promocode":"promo2",
"amount":"179"
}
];
var chartColors = window.chartColors;
var color = Chart.helpers.color;
var colors = [color(chartColors.red).alpha(0.5).rgbString(),
color(chartColors.orange).alpha(0.5).rgbString(),
color(chartColors.yellow).alpha(0.5).rgbString(),
color(chartColors.green).alpha(0.5).rgbString(),
color(chartColors.blue).alpha(0.5).rgbString()];
var bgColors = [];
const labels = Array.from(new Set(response.map(c => c.mmyy))).sort();
const promocodes = Array.from(new Set(response.map(c => c.promocode))).sort();
const datasets = promocodes.map(pc => ({ label: pc, data: [] }));
labels.forEach(l => {
for (let pc of promocodes) {
let city = response.find(c => c.mmyy == l && c.promocode == pc);
datasets.find(ds => ds.label == pc).data.push(city ? Number(city.amount) : 0);
}
});
var ctx = document.getElementById('promorChart').getContext('2d');
var chartColors = window.chartColors;
var color = Chart.helpers.color;
var promorChart = new Chart(ctx, {
type: 'bar',
data: {
labels: labels,
datasets: datasets
},
options: {
scales: {
xAxes: [{
stacked: false
}],
yAxes: [{
stacked: false,
ticks: {
// Include a dollar sign in the ticks
callback: function(value, index, values) {
return '$' + value;
}
}
}]
},
tooltips: {
callbacks: {
label: function(tooltipItems, data) {
return "$" + tooltipItems.yLabel.toString();
}
}
},
responsive: true,
elements: {
}
}
});
<canvas id="promorChart"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<script src="https://www.chartjs.org/samples/latest/utils.js"></script>
It works great, but as you can see, it needs some color, so I am trying to add some color with this code:
const labels = Array.from(new Set(response.map(c => c.mmyy))).sort();
const promocodes = Array.from(new Set(response.map(c => c.promocode))).sort();
const datasets = promocodes.map(pc => ({ label: pc, data: [], backgroundColor: bgColors }));
labels.forEach(l => {
for (var i = 0; i < labels.count.length; i++) {
let bgColors = (colors[i % colors.length]);
};
for (let pc of promocodes) {
let city = response.find(c => c.mmyy == l && c.promocode == pc);
datasets.find(ds => ds.label == pc).data.push(city ? Number(city.amount) : 0);
}
});
But that's not working. I'm getting the error TypeError: undefined is not an object (evaluating 'labels.count.length') Could someone tell me how to add a color for each of the labels properly form the color array properly? Thanks.

In the following code snippet, I work with simple color definitions for illustrating how it can be done. Simply replace the colors array with your own colors and it should work as expected.
const response = [{
"mmyy": "2019-12",
"promocode": "promo1",
"amount": "2776"
},
{
"mmyy": "2020-01",
"promocode": "promo1",
"amount": "1245"
},
{
"mmyy": "2020-01",
"promocode": "promo2",
"amount": "179"
}
];
const colors = ['red', 'orange', 'yellow', 'green', 'blue'];
const labels = Array.from(new Set(response.map(c => c.mmyy))).sort();
const promocodes = Array.from(new Set(response.map(c => c.promocode))).sort();
let i = 0;
const datasets = promocodes.map(pc => ({
label: pc,
data: [],
backgroundColor: colors[i++]
}));
labels.forEach(l => {
for (let pc of promocodes) {
let city = response.find(c => c.mmyy == l && c.promocode == pc);
datasets.find(ds => ds.label == pc).data.push(city ? Number(city.amount) : 0);
}
});
var ctx = document.getElementById('promorChart').getContext('2d');
var chartColors = window.chartColors;
var color = Chart.helpers.color;
var promorChart = new Chart(ctx, {
type: 'bar',
data: {
labels: labels,
datasets: datasets
},
options: {
scales: {
xAxes: [{
stacked: false
}],
yAxes: [{
stacked: false,
ticks: {
callback: (value, index, values) => '$' + value
}
}]
},
tooltips: {
callbacks: {
label: (tooltipItems, data) => "$" + tooltipItems.yLabel.toString()
}
},
responsive: true,
elements: {}
}
});
<canvas id="promorChart"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>

I just modified the code. You have to set up the backgroundcolor for custom bar colors.
//added for colors
function colorsFunction() {
return ['#3e95cd','#8e5ea2'];
}
const labels = Array.from(new Set(response.map(c => c.mmyy))).sort();
const promocodes = Array.from(new Set(response.map(c => c.promocode))).sort();
const datasets = promocodes.map(pc => ({ label: pc, data: [],backgroundColor: colorsFunction() })); //Modified here

add this property inside our datasets => backgroundColor: ['#3a95cd','#4e5ea2']
const response = [{
"mmyy": "2019-12",
"promocode": "promo1",
"amount": "2776"
},
{
"mmyy": "2020-01",
"promocode": "promo1",
"amount": "1245"
},
{
"mmyy": "2020-01",
"promocode": "promo2",
"amount": "179"
}
];
const labels = Array.from(new Set(response.map(c => c.mmyy))).sort();
const promocodes = Array.from(new Set(response.map(c => c.promocode))).sort();
const datasets = promocodes.map(pc => ({
label: pc,
data: [],
backgroundColor: ['#3a95cd','#4e5ea2'] // ['#3a95cd','#4e5ea2']
}));
labels.forEach(l => {
for (let pc of promocodes) {
let city = response.find(c => c.mmyy == l && c.promocode == pc);
datasets.find(ds => ds.label == pc).data.push(city ? Number(city.amount) : 0);
}
});
var ctx = document.getElementById('promorChart').getContext('2d');
var chartColors = window.chartColors;
var color = Chart.helpers.color;
var promorChart = new Chart(ctx, {
type: 'bar',
data: {
labels: labels,
datasets: datasets
},
options: {
plugins: {
colorschemes: {
scheme: 'brewer.DarkTwo3'
}
},
scales: {
xAxes: [{
stacked: false
}],
yAxes: [{
stacked: false,
ticks: {
callback: function(value, index, values) {
return '$' + value;
}
}
}]
},
tooltips: {
callbacks: {
label: function(tooltipItems, data) {
return "$" + tooltipItems.yLabel.toString();
}
}
},
responsive: true,
elements: {}
}
});
<canvas id="promorChart"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-colorschemes"></script>

Related

Render images as labels on Y axis

I'm using chart.js to graph my data.
I'm wondering if I can show the flag image/icon PNG for the labels.
function drawChart(obj, columnName, type = 'bar', selectedVal){
var ajax = $.ajax({url: '/query/' + obj + '/'+ columnName + '/' + selectedVal });
ajax.done(function (response) {
console.log('Response from API >>',response);
$('.lds-ripple').fadeOut();
var selector = `chart-${columnName}`;
var chartHtml = `<div class="col-sm-6"><canvas class="chart" id="${selector}" height="200"></canvas></div>`;
$('.charts').append(chartHtml);
keys = [];
values = [];
var length = 0
$.each(response, function(key,val) {
//console.log(key+val);
length++;
if(length<15){
keys.push(key);
values.push(val);
}
});
var showLegend = false;
if(type == 'doughnut' || type == 'radar' || type == 'polarArea' || type == 'pie'){
showLegend = true;
}
let chart = document.getElementById(selector).getContext('2d');
let xAxisTitle = columnName;
var sumOfAllValues = values.reduce((a, b) => a + b, 0);
Chart.defaults.global.defaultFontColor = "#fff";
var ticksDisplay = true;
if(columnName == 'country'){
ticksDisplay = false;
}
const images = ['https://i.stack.imgur.com/2RAv2.png', 'https://i.stack.imgur.com/Tq5DA.png', 'https://i.stack.imgur.com/3KRtW.png', 'https://i.stack.imgur.com/iLyVi.png'];
new Chart(chart, {
type: type, // bar, horizontalBar, pie, line, doughnut, radar, polarArea
plugins: [{
afterDraw: chart => {
var ctx = chart.chart.ctx;
var xAxis = chart.scales['x-axis-0'];
var yAxis = chart.scales['y-axis-0'];
xAxis.ticks.forEach((value, index) => {
var x = xAxis.getPixelForTick(index);
var y = yAxis.getPixelForTick(index);
var image = new Image();
image.src = images[index],
ctx.drawImage(image, x - 12, yAxis.bottom + 10);
// ctx.drawImage(image, x + 12, yAxis.left - 10);
});
}
}],
data:{
labels: keys,
datasets:[{
label:'Count',
data:values,
borderWidth:2,
hoverBorderWidth:2,
hoverBorderColor:'#fff',
color:'#fff',
backgroundColor: neonBgColors,
borderColor: neonBgBorders,
defaultFontColor: 'white'
}
]
},
options:{
title:{
display:true,
fontSize: 20,
text: columnName + '(' + sumOfAllValues + ')'
},
legend:{
display:showLegend,
position:'right',
labels:{
fontColor:'#000'
}
},
scales: {
xAxes: [{
ticks: {
precision:0,
beginAtZero: true
},
scaleLabel: {
display: true,
labelString: xAxisTitle + ' (' + sumOfAllValues + ')'
}
}],
yAxes: [{
ticks: {
precision:0,
beginAtZero: true,
display: ticksDisplay,
},
scaleLabel: {
display: true,
// labelString: 'Visitor Count'
}
}]
},
layout: {
padding: {
bottom: 30
}
},
}
});
});
}
I kept getting
I adapted the code of this answer and came up with the following solution for your case.
const labels = ['Red Vans', 'Blue Vans', 'Green Vans', 'Gray Vans'];
const images = ['https://i.stack.imgur.com/2RAv2.png', 'https://i.stack.imgur.com/Tq5DA.png', 'https://i.stack.imgur.com/3KRtW.png', 'https://i.stack.imgur.com/iLyVi.png']
.map(png => {
const image = new Image();
image.src = png;
return image;
});
const values = [48, 56, 33, 44];
new Chart(document.getElementById("myChart"), {
type: "horizontalBar",
plugins: [{
afterDraw: chart => {
var ctx = chart.chart.ctx;
var xAxis = chart.scales['x-axis-0'];
var yAxis = chart.scales['y-axis-0'];
yAxis.ticks.forEach((value, index) => {
var y = yAxis.getPixelForTick(index);
ctx.drawImage(images[index], xAxis.left - 40, y - 10);
});
}
}],
data: {
labels: labels,
datasets: [{
label: 'My Dataset',
data: values,
backgroundColor: ['red', 'blue', 'green', 'lightgray']
}]
},
options: {
responsive: true,
layout: {
padding: {
left: 50
}
},
legend: {
display: false
},
scales: {
yAxes: [{
ticks: {
display: false
}
}],
xAxes: [{
ticks: {
beginAtZero: true
}
}],
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="myChart" height="90"></canvas>

undefined labels piechart - chartjs

can you help me. i want show my data in percententage value. but my label is undefined. how i show my label to?
this my chart
var options = {
tooltips: {
custom: true
},
plugins: {
datalabels: {
display: true,
formatter: (value, ctx) => {
let datasets = ctx.chart.data.datasets;
if (datasets.indexOf(ctx.dataset) === datasets.length - 1) {
let sum = datasets[0].data.reduce((a, b) => a + b, 0);
let percentage = Math.round((value / sum) * 100) + '%';
return percentage;
} else {
return percentage;
}
},
color: '#fff',
}
}
};
var ctx = document.getElementById("pie-chart").getContext('2d');
var myChart = new Chart(ctx, {
type: 'pie',
data: {
datasets: data
},
options: options
});
Labels array is not supposed to be in the dataset itself but is supposed to be in the main data object like so:
var data = [{
data: [50, 55, 60, 33],
backgroundColor: [
"#4b77a9",
"#5f255f",
"#d21243",
"#B27200"
],
borderColor: "#fff"
}];
var options = {
plugins: {
datalabels: {
formatter: (value, ctx) => {
let sum = 0;
let dataArr = ctx.chart.data.datasets[0].data;
dataArr.map(data => {
sum += data;
});
let percentage = (value * 100 / sum).toFixed(2) + "%";
return percentage;
},
color: '#fff',
}
}
};
var ctx = document.getElementById("pie-chart").getContext('2d');
var myChart = new Chart(ctx, {
type: 'pie',
data: {
labels: ["India", "China", "US", "Canada"],
datasets: data
},
options: options
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels#0.4.0/dist/chartjs-plugin-datalabels.min.js"></script>
<canvas id="pie-chart"></canvas>

Why parents of level 2 are linking to same child nodes either their child are different in organization Highcharts

I am using organization Highcharts in which the parents on Level 2 are linking to same childs.
As you can see in the picture Nurrettin & Mohammad Sabir nodes linked to all same child nodes. However Nurretin is a parent of Cemil Ceylan & Mustafa while M.Sabir is a parent of remaining 7 child nodes.
But issue is why Nurretin node is linked to all?
Can somebody answer it ?
Organization Highcharts
The Code is Here
renderSubordinates=(data)=>{
console.log('data',data)
var array2D =[];
var arraySeries2D =[];
var arr = [];
var levelObj;
var arrSeries = [];
const rec = (val) => {
let name = val.from;
for(let j=0;j<val.to.length;j++){
let nameChild = val.to[j].from;
array2D.push([name,nameChild])
}
return [...array2D]
}
const createHC = (data) => {
arr.push(rec(data));
if(data.to.length){
data.to.map(x=>{
arr.push(rec(x));
createHC(x);
})
}
}
var data1 = data[0];
createHC(data1);
console.log('arr',arr.pop())
var dataArr = arr.pop()
var levelInd = arr;
var levelArr =levelInd.map((x,index)=>{
// console.log('index',index);
levelObj={
level: index,
color: {
linearGradient: { x1: 0, x2: 1, y1: 0, y2: 1 },
stops: [
[0, "#0376b0"],
[1, "#b4e5fe"],
],
},
dataLabels: {
color: 'white',
style:{
fontSize:'10px'
}
}}
return levelObj
})
const seriesRec = (val) => {
let obj = {id:val.name,title:val.title,name:val.name,image:val.image,
dataLabels: {
enabled: true,
style: {
fontSize: '10px'
}
}};
arraySeries2D.push(obj)
return [...arraySeries2D]
}
const seriesHC = (data) => {
arrSeries.push(seriesRec(data));
if(data.to.length){
data.to.map(x=>{
arrSeries.push(seriesRec(x));
seriesHC(x);
})
}
}
var data1 = data[0];
seriesHC(data1);
var dataSeriesArr = arrSeries.pop()
let obj: Highcharts.Options = {
chart: {
height: 1200,
inverted: true
},
title: {
text: ''
},
series: [{
type: 'organization',
name: '',
keys: ['from', 'to'],
data: dataArr,
levels: levelArr,
nodes:dataSeriesArr,
colorByPoint: false,
color: '#007ad0',
dataLabels: {
color: 'white'
},
borderColor: '',
borderRadius:20,
nodeWidth: 120,
className:'node-class'
}],
tooltip: {
outside: true
},
plotOptions: {
organization: {
linkColor: 'gray',
linkLineWidth:2,
}
},
exporting: {
allowHTML: true,
sourceWidth: 800,
sourceHeight: 600
}
};
return obj
}

Click on chartjs pie slice to hide the slice

I'm trying to get chartjs to hide pie slices on click; the same behavior seen when clicking on the legend.
Here is the code I'm using - I commented out the code that actually removes the data from the chart since it doesn't re-render the pie chart correctly.
const values = [50, 55, 60, 33];
const data = {
labels: ["India", "China", "US", "Canada"],
datasets: [{
data: values,
backgroundColor: [
"#4b77a9",
"#5f255f",
"#d21243",
"#B27200"
],
borderColor: "#fff"
}]
};
const options = {
tooltips: {
enabled: false
},
legend: {
enabled: true,
position: 'bottom'
},
animation: false,
onClick: handleClick,
plugins: {
datalabels: {
formatter: (value = 0, ctxx) => {
const sum = values.reduce((acc, val = 0) => (acc + val), 0);
return `${(value * 100 / sum).toFixed(2)}%`;
},
color: '#fff',
}
}
};
const ctx = document.getElementById("pie-chart").getContext('2d');
const myChart = new Chart(ctx, {
type: 'pie',
data,
options: options
});
function handleClick(e, slice) {
setTimeout(() => {
const legend = myChart.legend.legendItems;
if (slice && slice[0]) {
// get clicked on slice index
const sliceIndex = slice[0]._index;
// Removing the data "really" messes up the chart
// myChart.data.labels.splice(sliceIndex, 1);
// myChart.data.datasets[0].data.splice(sliceIndex, 1);
legend[sliceIndex].hidden = true;
chart.update();
}
const visibleSlices = legend.filter(l => !l.hidden);
console.log('visible slices', visibleSlices);
});
}
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels#0.4.0/dist/chartjs-plugin-datalabels.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="pie-chart"></canvas>
It seems like setting the hidden flag then updating the chart should work because that's what the plugin is doing: https://github.com/chartjs/Chart.js/blob/master/src/plugins/plugin.legend.js#L20-L30.
The code below is hiding a slice by the index
myChart.getDatasetMeta(0).data[sliceIndex].hidden = true
const values = [50, 55, 60, 33];
const data = {
labels: ["India", "China", "US", "Canada"],
datasets: [{
data: values,
backgroundColor: [
"#4b77a9",
"#5f255f",
"#d21243",
"#B27200"
],
borderColor: "#fff"
}]
};
const options = {
tooltips: {
enabled: false
},
legend: {
enabled: true,
position: 'bottom'
},
animation: false,
onClick: handleClick,
plugins: {
datalabels: {
formatter: (value = 0, ctxx) => {
const sum = values.reduce((acc, val = 0) => (acc + val), 0);
return `${(value * 100 / sum).toFixed(2)}%`;
},
color: '#fff',
}
}
};
const ctx = document.getElementById("pie-chart").getContext('2d');
const myChart = new Chart(ctx, {
type: 'pie',
data,
options: options
});
function handleClick(e, slice) {
setTimeout(() => {
const legend = myChart.legend.legendItems;
if (slice && slice[0]) {
const sliceIndex = slice[0]._index;
myChart.getDatasetMeta(0).data[sliceIndex].hidden = true
myChart.update();
}
});
}
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels#0.4.0/dist/chartjs-plugin-datalabels.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="pie-chart"></canvas>

Visualising my mapped object with Charts.js

I have a map object: As you can see it is nesting by year-month-day.
I would like to create a bar chart where you can see those numbers for "Keszeg", "Ponty"..etc based on the year-month-day.
My code is ready but i can't get it working. At this part i am getting undefinied error for the yearVal.entries.
This is the sulyhavonta.entries:
for (let [yearKey, yearVal] of sulyhavonta.entries()) {
for (let [monthKey, monthVal] of yearVal.entries())
let labels = [];
let datasets = [];
let fishData = {};
this.firebasedb.list("/fogasok/").subscribe(_data => {
// this.osszesfogasadatok = _data.filter(item => item.publikus == true);
// let fogasSzam=this.osszesfogasadatok.length;
let sulySum = _data.reduce((sum, item) => sum + parseInt(item.suly), 0);
let sulySumMap = _data.map((item, index) => {
var n = new Date(item.datum);
return {
ev: n.getFullYear(),
honap: n.getMonth() + 1,
nap: n.getDate(),
suly: item.suly,
halfaj: item.halfaj,
eteto: item.etetoanyag1,
csali: item.hasznaltcsali,
helyszin: item.helyszin
}
});
var sulySumByDate = d3.nest()
.key(function (d) {
return d.ev;
})
.key(function (d) {
return d.honap;
})
.key(function (d) {
return d.nap;
})
.key(function (d) {
return d.halfaj;
})
.rollup(function (values) {
return d3.sum(values, function (d) {
return parseInt(d.suly);
});
})
.map(sulySumMap)
var sulyhavonta=sulySumByDate;
console.log("sulyhavonta",sulyhavonta)
for (let [yearKey, yearVal] of sulyhavonta.entries()) {
for (let [monthKey, monthVal] of yearVal.entries()) {
for (let [dayKey, dayVal] of monthVal.entires()) {
labels.push(yearKey + '.' + monthKey + '.' + dayKey);
for (let [fish, fishVal] of dayVal.entires()) {
if (fishData[fish] === undefined) {
fishData[fish] = [];
}
fishData[fish].push(fishVal);
console.log("fishdata",fishData);
}
}
}
}
var colors = [
["#ce8d00", "#ffae00"],
["#007bce", "#84ceff"]
];
var i = 0;
for (let key in fishData) {
datasets.push({
label: key,
data: fishData[key],
backgroundColor: colors[i % 2][0],
hoverBackgroundColor: colors[i % 2][1],
hoverBorderWidth: 0
});
i++;
}
console.log("dataset",datasets)
});
var bar_ctx = document.getElementById('bar-chart');
var bar_chart = new Chart(bar_ctx, {
type: 'bar',
data: {
labels: labels,
datasets: datasets
},
options: {
animation: {
duration: 10,
},
scales: {
xAxes: [{
stacked: true,
gridLines: {
display: false
},
}],
yAxes: [{
stacked: true
}],
}, // scales
legend: {
display: true
}
} // options
});
Just a quick note, instead of the reading from firebase and d3 nesting, it is working with static data, but i would like to use the code above to directly read from my database.
var sulyhavonta = new Map([ [2018, new Map([ [1,new Map([[15, new
Map([['keszeg',3],['ponty',5]])], [29, new
Map([['keszeg',1],['ponty',1]])]])], [5,new Map([[24, new
Map([['keszeg',9],['ponty',7]])]])] ] )] ]);

Categories

Resources