Chart.js not showing dynamically populated data - javascript

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", ]
}]

Related

chart.js dynamically adjust gap between vertical bars

I am working on a webpage that presents dashboard on th basis of invoice processing project.
using below code i am populating bar chart.
function loadVolumeChart()
{
var pieChartContent = document.getElementById('chartAreaWrapper');
pieChartContent.innerHTML = '';
$('#chartAreaWrapper').append('<canvas id="line-chart" height="300" width="1500px"><canvas>');
//getData For Volume Analysis Chary
var url_string = document.referrer;;
var url = new URL(url_string);
var name = url.searchParams.get("name");
var user=url.searchParams.get("user");
var team=url.searchParams.get("team");
var date=url.searchParams.get("date");
var dates = [];
var count = [];
var from = date.split("-")[0];
var to = date.split("-")[1];
var re=$.ajax({
url: 'getTotalCounts.php',
type: 'POST',
data: {
from:from,
to:to,
team:team,
totalVolume: '00'
},
async:false,
success: function(data) {
var result =data;
var json = JSON.parse(result);
dates=json[0].data;//json[0].data;
count=json[1].data;
//alert(dates);
}
}).done(function(data){
// openPage(data);
}).fail(function(data){
alert(data.responseText);
});
//volume chart
new Chart(document.getElementById("line-chart"), {
type: 'bar', //line
data: {
labels:dates,
datasets: [{
data:count,
label: "Total Inward",
backgroundColor: "#0E6655", //borderColor
fill: true
},
]
},
options: {
responsive:false,
maintainAspectRatio: true,
legend: {
display: false
},
tooltips: {
enabled: true
},
scales: {
xAxes: [{
gridLines: {
display:false
},
barThickness: 15,
}],
yAxes: [{
barPercentage: 1.0,
categoryPercentage: 1.0,
gridLines: {
display:false
},
ticks: {
min: 0,
max:10,
stepSize: 1
}
}]
},
}
});
//end of volume chart
}
But the problem is when data is low, means if the x axis data contains only 2 dates, then the gap between two bars is too large, like the image below,
but if i add more dates then the gap reduces.i want to set gap between two bars even if their are only two bars. the gap between both should not increase if the dates (bars) according to the size of x axis data. if the data is large then it should only scroll. thats why i have added scroll bar.
the div of chart is as:
<div class="parentDiv" >
<div class="chartAreaWrapper" id="chartAreaWrapper" style="height:80%;width:70%;margin-left: 20px; margin-top: 10px;float: left;">
<canvas id="line-chart" height="300" width="1500px"></canvas>
</div>
</div>
In your code, the option xAxis.barThickness defines that the width of individual bars has to be of 15 pixels. Simply remove this option.
You should also consider to use the latest stable version of Chart.js (currently v2.9.3) where the option xAxis.barThickness is deprecated. The options barThickness, barPercentage and categoryPercentage are now part of the dataset configuration.

how to pass an array to bar chart.js

I have drawn charts using morris.js which is pretty easy, but I want to switch to charts.js library. I have an array which has 'date' and two other values, I want to plot the date values on x-axis and the rest two on y-axis using chart.js bar function.
In morris it was easy to define values separately for both the axis but I don't understand how to do this in chart.js.
function testChart(myArray){
new Chart(document.getElementById("churn-bar-chart"), {
type: 'bar',
myArray: {
labels: 'date',
datasets: [
{
backgroundColor: ["#3e95cd", "#8e5ea2","#3cba9f","#e8c3b9","#c45850"],
data: ['new', 'lost']
}
]
},
options: {
legend: { display: true },
title: {
display: true,
text: 'Predicted world population (millions) in 2050'
}
}
});
}
// myArray
var UserArray = new Array();
UserArray= [];
var obj2 = {};
obj2.date = Date;
obj2.new = newUser;
obj2.lost = lostUser;
UserArray.push(obj2);

Filter a legend Item with Chartjs.org V2.7

I am building a series of doughnut charts and I would like to remove the second item in the legend, so when I generate the legend with the generateLegend() method I just want to get the first value.
In the documentation there is an option that reads
Filters legend items out of the legend. Receives 2 parameters, a Legend Item and the chart data
But I can't find an example how to use it. In this Pen you can see the 2 labels in the middle, I just want to show the first label. I tried different approaches with no success. Just deleting the item doesn't work for me because the <li> item still there. Here's the code I am using.
$id = function(id) {
return document.getElementById(id);
};
var langDataEs = {
type: "doughnut",
data: {
datasets: [
{
data: [75, 25],
backgroundColor: ["#8dc63f", "#1d1d1d"]
}
],
labels: ["es", "learning"]
},
options: {
legend: {
display: false,
/* I would like to remove the item "learning" */
filter: function() {
},
},
responsive: true
}
};
langChartEs = new Chart($id("langEs").getContext("2d"), langDataEs);
$id("es").innerHTML = langChartEs.generateLegend();
Thanks in advance for any pointers.
The filter function works exactly like the Javascript's native Array.prototype.filter. So just return true if you want to show the legend at a particular index.
EDIT: The filter function would come inside the labels field.
legend: {
display: true,
labels: {
filter: function(legendItem, data) {
return legendItem.index != 1
}
}
}

Chart.js: Bar Chart Click Events

I've just started working with Chart.js, and I am getting very frustrated very quickly. I have my stacked bar chart working, but I can't get the click "events" to work.
I have found a comment on GitHub by nnnick from Chart.js stating to use the function getBarsAtEvent, even though this function cannot be found in the Chart.js documentation at all (go ahead, do a search for it). The documentation does mention the getElementsAtEvent function of the chart reference, but that is for Line Charts only.
I set an event listener (the right way) on my canvas element:
canv.addEventListener('click', handleClick, false);
...yet in my handleClick function, chart.getBarsAtEvent is undefined!
Now, in the Chart.js document, there is a statement about a different way to register the click event for the bar chart. It is much different than nnnick's comment on GitHub from 2 years ago.
In the Global Chart Defaults you can set an onClick function for your chart. I added an onClick function to my chart configuration, and it did nothing...
So, how the heck do I get the on-click-callback to work for my bar chart?!
Any help would be greatly appreciated. Thanks!
P.S.: I am not using the master build from GitHub. I tried, but it kept screaming that require is undefined and I was not ready to include CommonJS just so that I could use this chart library. I would rather write my own dang charts. Instead, I downloaded and am using the Standard Build version that I downloaded straight from the link at the top of the documentation page.
EXAMPLE: Here is an example of the configuration I am using:
var chart_config = {
type: 'bar',
data: {
labels: ['One', 'Two', 'Three'],
datasets: [
{
label: 'Dataset 1',
backgroundColor: '#848484',
data: [4, 2, 6]
},
{
label: 'Dataset 2',
backgroundColor: '#848484',
data: [1, 6, 3]
},
{
label: 'Dataset 3',
backgroundColor: '#848484',
data: [7, 5, 2]
}
]
},
options: {
title: {
display: false,
text: 'Stacked Bars'
},
tooltips: {
mode: 'label'
},
responsive: true,
maintainAspectRatio: false,
scales: {
xAxes: [
{
stacked: true
}
],
yAxes: [
{
stacked: true
}
]
},
onClick: handleClick
}
};
I managed to find the answer to my question by looking through the Chart.js source code.
Provided at line 3727 of Chart.js, Standard Build, is the method .getElementAtEvent. This method returns me the "chart element" that was clicked on. There is sufficent data here to determine what data to show in a drill-down view of the dataset clicked on.
On the first index of the array returned by chart.getElementAtEvent is a value _datasetIndex. This value shows the index of the dataset that was clicked on.
The specific bar that was clicked on, I believe, is noted by the value _index. In my example in my question, _index would point to One in chart_config.data.labels.
My handleClick function now looks like this:
function handleClick(evt)
{
var activeElement = chart.getElementAtEvent(evt);
..where chart is the reference of the chart created by chart.js when doing:
chart = new Chart(canv, chart_config);
The specific set of data that was selected by the click can therefore be found as:
chart_config.data.datasets[activeElement[0]._datasetIndex].data[activeElement[0]._index];
And there you have it. I now have a datapoint that I can build a query from to display the data of the bar that was clicked on.
AUGUST 7TH, 2021. UPDATE
There is now a method for what we are looking for. Take a look at here
Hi this is the click event under options which is getting values from x and y-axis
onClick: function(c,i) {
e = i[0];
console.log(e._index)
var x_value = this.data.labels[e._index];
var y_value = this.data.datasets[0].data[e._index];
console.log(x_value);
console.log(y_value);
}
I found this solution at https://github.com/valor-software/ng2-charts/issues/489
public chartClicked(e: any): void {
if (e.active.length > 0) {
const chart = e.active[0]._chart;
const activePoints = chart.getElementAtEvent(e.event);
if ( activePoints.length > 0) {
// get the internal index of slice in pie chart
const clickedElementIndex = activePoints[0]._index;
const label = chart.data.labels[clickedElementIndex];
// get value by index
const value = chart.data.datasets[0].data[clickedElementIndex];
console.log(clickedElementIndex, label, value)
}
}
}
You can use onClick like this.
var worstCells3GBoxChart = new Chart(ctx, {
type: 'bar',
data: {
labels: lbls,
datasets: [{
label: 'Worst Cells by 3G',
data: datas,
backgroundColor: getColorsUptoArray('bg', datas.length),
borderColor: getColorsUptoArray('br', datas.length),
borderWidth: 1
}]
},
options: {
legend: {
display: false
},
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
},
onClick: function (e) {
debugger;
var activePointLabel = this.getElementsAtEvent(e)[0]._model.label;
alert(activePointLabel);
}
}
});
Chartjs V3.4.1
This is what worked for me in v3, after looking at solutions for older versions:
const onClick = (event, clickedElements) => {
if (clickedElements.length === 0) return
const { dataIndex, raw } = clickedElements[0].element.$context
const barLabel = event.chart.data.labels[dataIndex]
...
}
raw is the value of the clicked bar.
barLabel is the label of the clicked bar.
You need to pass the onClick to the bar chart config:
const barConfig = {
...
options: {
responsive: true,
onClick,
...
}
}
Well done! This seems to return the data value being charted though, which in many cases might be possible to appear more than once, thus making it unclear what was clicked on.
This will return the actual data label of the bar being clicked on. I found this more useful when drilling down into a category.
chart_config.data.labels[activeElement[0]._index]
I was able to make this work in another way.
Might not be supported, but sometimes, I find that neither the label nor the value is adequate to get me the necessary information to populate a drill-through.
So what I did was add a custom set of attributes to the data:
var ctx = document.getElementById("cnvMyChart").getContext("2d");
if(theChart != null) {theChart.destroy();}
theChart = new Chart(ctx, {
type: typ,
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datakeys: ["thefirstone","thesecondone","thethirdone","thefourthone","thefifthone","thesixthone"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
...etc
Then when I need to push the drillthrough key into another ajax call, I was able to get it with this:
var theDrillThroughKey = theChart.config.data.datakeys[activePoints[0]._index];
So I'm really not sure that it's appropriate to be adding custom elements into the data for the Chart, but it's working so far in Chrome, IE and Firefox. I needed to be able to put more information into the drillthrough than I really wanted displayed.
Example of the full thing: https://wa.rrdsb.com/chartExamples
Thoughts?
I had the same problem with multiple datasets, and used this workaround:
var clickOnChart = function(dataIndex){
...
}
var lastHoveredIndex = null;
var chart_options = {
...
tooltips: {
...
callbacks: {
label: function(tooltipItem, chart) {
var index = tooltipItem.datasetIndex;
var value = chart.datasets[index].data[0];
var label = chart.datasets[index].label;
lastHoveredIndex = index;
return value + "€";
}
}
},
onClick:function(e, items){
if ( items.length == 0 ) return; //Clicked outside any bar.
clickOnChart(lastHoveredIndex);
}
}
Let's say that you declared a chart using a method like so:
window.myBar = new Chart({chart_name}, {
type: xxx,
data: xxx,
events: ["click"],
options: {
...
}
});
A good way of declaring onclick events would involve listening for the canvas click, like so:
({chart_name}.canvas).onclick = function(evt) {
var activePoints = myBar.getElementsAtEvent(evt);
// let's say you wanted to perform different actions based on label selected
if (activePoints[0]._model.label == "label you are looking for") { ... }
}
In the chart options for Chart.js v3.5.1 which is latest
Check below sample code
let enterpriseChartOptions = {
responsive:true,
maintainAspectRatio: false,
onClick: (c,i) => {
console.log('Get the underlying label for click,', c.chart.config._config.data.labels[i[0].index]);
},
plugins: {
title:{
text:'Enterprise Dashboard (Health Status of 10 stores) updated every 30 minutes',
fontSize:20
},
},
scales: {
x: {
display: true,
type: 'category',
position: 'right',
ticks: {
padding: 8,
},
},
y: {
display: true,
ticks: {
callback: function(val, index) {
// Show the label
return val < 1 ? "All good" : (val < 2 && val >=1) ? "Warning": val === 2 ? "Critical" : "";
},
//color: 'red',
stepSize: 1,
padding: 8
}
}
},
layout: {
padding: {
left: 20,
right: 20,
top: 25,
bottom: 0
}
},
};
var employeeDetailsCtx = document.getElementById("employee-details").getContext("2d");
var employee_details_data = {
labels: ["Late Present", "On Leave", "Training", "Tour"],
datasets: [{
label: "Officer",
backgroundColor: "#5A8DEE",
data: [
...
]
}, {
label: "Staff",
backgroundColor: "#4BC0C0",
data: [
...
]
}]
};
var myoption = {
tooltips: {
enabled: true
},
hover: {
animationDuration: 1
},
onClick: function (evt, i) {
var activePoint = employeeDetailsBarChart.getElementAtEvent(evt)[0];
var data = activePoint._chart.data;
var datasetIndex = activePoint._datasetIndex;
var label = data.datasets[datasetIndex].label;
var value = data.datasets[datasetIndex].data[activePoint._index];
e = i[0];
var x_value = this.data.labels[e._index];
console.log(x_value)
console.log(label)
console.log(value)
},
animation: {
duration: 1,
onComplete: function () {
var chartInstance = this.chart,
ctx = chartInstance.ctx;
ctx.textAlign = 'center';
ctx.fillStyle = "rgba(0, 0, 0, 1)";
ctx.textBaseline = 'bottom';
this.data.datasets.forEach(function (dataset, i) {
var meta = chartInstance.controller.getDatasetMeta(i);
meta.data.forEach(function (bar, index) {
var data = dataset.data[index];
ctx.fillText(data, bar._model.x, bar._model.y - 5);
});
});
}
}
};
var employeeDetailsBarChart = new Chart(employeeDetailsCtx, {
type: 'bar',
data: employee_details_data,
options: myoption
});

How do I skip a datapoint in Highcharts without causing an interruption in the line of the chart?

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

Categories