I'm essentially attempting to create a bar chart with 2-8 items where the label on the bottom/legend is the short product code(ex: 4380) and mousing over the bar shows the full SKU/product name.
I have gotten it mostly working but my implementation goes one of two undesirable ways.
The data points all combine into the first product number/chart label.
The blank spots make the bars tiny/not fill up the full width.
My code for rendering the chart is as follows:
var myBarChart2;
$.ajax({
url: "chartdata.php",
data: {
"skugroup": group
},
method: 'GET',
dataType: 'json',
success: function (d) {
Chart.defaults.global.defaultFontFamily = '-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif';
Chart.defaults.global.defaultFontColor = '#292b2c';
var ctx = document.getElementById("inventorybarchart");
myBarChart2 = new Chart(ctx, {
type: 'bar',
data: {
labels: d.labels,
datasets: d.datasets,
},
options: {
scales: {
xAxes: [{
gridLines: {
display: false
},
ticks: {
display: true
}
}],
yAxes: [{
ticks: {
min: 0,
beginAtZero: true
},
gridLines: {
display: true
}
}],
},
legend: {
display: false
}
}
});
}
});
The ajax response for the two versions is as follows:
Version 1:
{"datasets":[{"labels":"GRAY-DARK-GRAY","backgroundColor":"rgba(164,222,164,1)","borderColor":"rgba(164,222,164,1)","data":[5996]},{"labels":"CANARY-YELLOW","backgroundColor":"rgba(35,148,58,1)","borderColor":"rgba(35,148,58,1)","data":[4605]},{"labels":"PINK-WHITE-GRAY","backgroundColor":"rgba(101,24,125,1)","borderColor":"rgba(101,24,125,1)","data":[1288]},{"labels":"SEAFOAM-WHITE-GRAY","backgroundColor":"rgba(129,74,64,1)","borderColor":"rgba(129,74,64,1)","data":[3463]},{"labels":"YELLOW-WHITE-GRAY","backgroundColor":"rgba(91,216,70,1)","borderColor":"rgba(91,216,70,1)","data":[1537]},{"labels":"WHITE-YELLOW","backgroundColor":"rgba(101,225,237,1)","borderColor":"rgba(101,225,237,1)","data":[152]}],"labels":["4380","4311","4571","4588","4557","4373"]}
Version 2:
{"datasets":[{"label":"GRAY-DARK-GRAY","backgroundColor":"rgba(1,1,235,1)","borderColor":"rgba(1,1,235,1)","data":[5996,null,null,null,null]},{"label":"CANARY-YELLOW","backgroundColor":"rgba(12,87,184,1)","borderColor":"rgba(12,87,184,1)","data":[null,4605,null,null,null]},{"label":"PINK-WHITE-GRAY","backgroundColor":"rgba(85,107,126,1)","borderColor":"rgba(85,107,126,1)","data":[null,null,1288,null,null]},{"label":"SEAFOAM-WHITE-GRAY","backgroundColor":"rgba(181,150,65,1)","borderColor":"rgba(181,150,65,1)","data":[null,null,null,3463,null]},{"label":"YELLOW-WHITE-GRAY","backgroundColor":"rgba(132,66,28,1)","borderColor":"rgba(132,66,28,1)","data":[null,null,null,null,1537]},{"label":"WHITE-YELLOW","backgroundColor":"rgba(49,195,217,1)","borderColor":"rgba(49,195,217,1)","data":[null,null,null,null,null]}],"labels":["4380","4311","4571","4588","4557","4373"]}
The only difference is either I always use the 0 indexes for datasets[index].data or I fill in null depending on where it should be.
Should I be changing the way the chart is rendered or should I change the way the data is passed in?
For the record, the mouseover shows the proper sku/full name.
I would define the data in a single dataset and keep the full product names in a separate property.
const data = {
"labels": ["4380", "4311", "4571", "4588", "4557", "4373"],
"productNames": ["GRAY-DARK-GRAY", "CANARY-YELLOW", "PINK-WHITE-GRAY", "SEAFOAM-WHITE-GRAY", "YELLOW-WHITE-GRAY", "WHITE-YELLOW"],
"datasets": [{
"data": [5996, 4605, 1288, 3463, 1537, 152],
...
}]
};
To get the product names displayed in the tooltip, you would have to define a label callback function as follows:
tooltips: {
callbacks: {
label: (tooltipItem, data) => {
let i = tooltipItem.index;
return data.productNames[i] + ': ' + data.datasets[0].data[i];
}
}
}
Please take a look at your amended code and see how it works.
const data = {
"labels": ["4380", "4311", "4571", "4588", "4557", "4373"],
"productNames": ["GRAY-DARK-GRAY", "CANARY-YELLOW", "PINK-WHITE-GRAY", "SEAFOAM-WHITE-GRAY", "YELLOW-WHITE-GRAY", "WHITE-YELLOW"],
"datasets": [{
"data": [5996, 4605, 1288, 3463, 1537, 152],
"backgroundColor": ["rgba(1,1,235,1)", "rgba(12,87,184,1)", "rgba(85,107,126,1)", "rgba(181,150,65,1)", "rgba(132,66,28,1)", "rgba(49,195,217,1)"],
"borderColor": ["rgba(1,1,235,1)", "rgba(12,87,184,1)", "rgba(85,107,126,1)", "rgba(181,150,65,1)", "rgba(132,66,28,1)", "rgba(49,195,217,1)"]
}]
};
var ctx = document.getElementById("inventorybarchart");
myBarChart2 = new Chart(ctx, {
type: 'bar',
data: data,
options: {
scales: {
xAxes: [{
gridLines: {
display: false
}
}],
yAxes: [{
ticks: {
beginAtZero: true
}
}],
},
legend: {
display: false
},
tooltips: {
callbacks: {
label: (tooltipItem, data) => {
let i = tooltipItem.index;
return data.productNames[i] + ': ' + data.datasets[0].data[i];
}
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.0/Chart.min.js"></script>
<canvas id="inventorybarchart" height="90"></canvas>
I was having some trouble when trying to dynamically populate bar chart in chart.js. I have two arrays, one for label, one for its price and both of them are already populated with the sorted data from firebase. Here is my code:
var ctx = document.getElementById('brandChart').getContext("2d");
var data = {
labels: [],
datasets: [{
data: [],
backgroundColor: [
"#424242",
]
}]
};
var options = {
layout: {
padding: {
top: 5
}
},
responsive: true,
legend: {
display: true,
position: 'bottom',
// disable legend onclick remove slice
onClick: null
},
animation: {
animateScale: true,
animateRotate: true
},
};
var opt = {
type: "horizontalBar",
data: data,
options: options
};
if (brandChart) brandChart.destroy();
brandChart = new Chart(ctx, opt);
// dynamically populate chart
for(var i = 0; i < labelData.length; i++){
brandChart.config.data.labels.push(labelData[i]);
}
for(var i = 0; i < priceData.length; i++){
brandChart.config.data.datasets[0].data.push(priceData[i]);
}
brandChart.update();
I managed to show all of them in bar chart, however, the result as such:
It is kind of squeeze between each labels if there are too many categories. Also, only the first bar has the color & the legends shown undefined. Any ideas how to solve these?
ɪꜱꜱᴜᴇ #1 - ꜱᴏʟᴜᴛɪᴏɴ
Add a callback for y-axis ticks, in your chart options :
options: {
scales: {
yAxes: [{
ticks: {
callback: function(t, i) {
if (!(i % 2)) return t;
}
}
}]
},
...
}
this will only show every other label on y-axis.
ɪꜱꜱᴜᴇ #2 - ꜱᴏʟᴜᴛɪᴏɴ
This is because, you have only one color in your backgroundColor array. If you want different color for each bar, then you need to populate this array with multiple color values.
Edit: as it seems form your updated question, you already kind of got the idea.
ɪꜱꜱᴜᴇ #3 - ꜱᴏʟᴜᴛɪᴏɴ
Define the label property for your dataset , like so :
datasets: [{
label: 'Legend Title', //<- define this
data: [],
backgroundColor: ["#424242", ]
}]
I'm trying to remove the space between my bar chart bars, but even though I see this solution many places it doesn't work for me. It's also not mentioned in the Chart.js docs so that is odd. Can someone tell me how to specify it?
var options = {
barValueSpacing : 1, // doesn't work; find another way
barDatasetSpacing : 1, // doesn't work; find another way
legend: {
display: false // Hides annoying dataset label
},
tooltips: {
callbacks: {
label: function(tooltipItem) {
return tooltipItem.yLabel;
}
}
}
};
var ctx = document.getElementById("canvasX").getContext("2d");
var myBarChart = new Chart(ctx, {
type: 'bar',
data: data,
options: options
});
You need to set barPercentage and categoryPercentage to 1.0 on the x-axis scale. Add this to your options object:
var options = {
...
scales: {
xAxes: [{
categoryPercentage: 1.0,
barPercentage: 1.0
}]
}
};
See http://www.chartjs.org/docs/#bar-chart-chart-options
In version 3.2.0 set barPercentage and categoryPercentage to 1.0 within each data set:
var datasets = [
{
...
barPercentage: 1.0,
categoryPercentage: 1.0
}
]
See https://www.chartjs.org/docs/3.2.0/charts/bar.html for more details
Both barpercentage and categorypercentage are property of dataset option in chart.js. You can see them in the list here and its default value.
const labels = ["AA","BB", "CC", "DD", "EE", "FF"];
const data = {
labels: labels,
datasets: [{
categoryPercentage: 0.8, // notice here
barPercentage: 0.8, // notice here
label: 'Male',
data: [-189, -97, -2076, -691, -7887, -3687],
//...
}]
};
However, according to the configuration document,
The dataset options can be changed at multiple different levels.
Just like the example below. the option can be used in global configuration level.
const config = {
type: 'bar',
data: data,
options: {
categoryPercentage: 1.0, // here
barPercentage: 0.98, // here
maintainAspectRatio: false,
indexAxis: 'y',
scales: {
y: {
beginAtZero: true,
stacked: true
},
}
},
};
If you are interested in how the two property interacts, please see the detail here.
Using Highcharts ...
I have a series of values that corresponds with a series of times. Sometimes, the data value is null on a specific time and no marker is placed on the chart, which is good, but the line gets interrupted and basically starts a new chart on the next value that is not null.
I would like to skip the null values on the chart, but still keep the time values and just connect the last non-null value to the next with a solid line.
I have tried skipping the entire index if the value is null, but then the time value is skipped as well.
I have replaced the null with 0, but obviously then the data points lands on the 0-axis.
This is my data:
for (i = 0; i < tpoints.length; i++)
{
var tc = cpoints[i];
var tf = fpoints[i];
var t = tpoints[i];
labels.push(t);
c.push(tc);
f.push(tf);
}
This is my chart config:
var config = {
type: 'line',
fill : false,
data: {
datasets: [{
data: f,
backgroundColor: [
color(window.chartColors.red).alpha(0.5).rgbString(),
],
label: '',
fill : false
},{
data: c,
backgroundColor: [
color(window.chartColors.blue).alpha(0.5).rgbString(),
],
label: '',
fill : false
}],
labels: labels
},
options: {
maintainAspectRatio: false,
responsive: true,
title: {
display: true,
text: title,
fontSize: 22
},
legend: {
position: "bottom",
display: false
},
scales: {
yAxes: [{
display: true,
scaleLabel: {
display: true,
labelString: 'T'
},
ticks: {
beginAtZero: true
}
}]
}
}
};
This creates the line-chart as expected (Color dots for each data point. Each data dot is then connected with a thin grey line.), but somewhere in the middle of the chart, if it receives a null value, the grey line gets cut and only displays again between two non-null values.
Any ideas on how i can connect the data point before the null with the data point after the null?
Enable the connectNulls option:
series: [{
data: [...],
connectNulls: true
}]
Live demo: http://jsfiddle.net/BlackLabel/fheb9yp5/
API Reference: https://api.highcharts.com/highcharts/series.line.connectNulls
can you show us how your data shows up? if the null values have a meaning in your treatments I suggest you change the type of graph or replace the null values by zero