Category on chartjs graph shows undefined - javascript

// Get a reference to the form element
const form = document.querySelector('form');
// Get a reference to the canvas element
const canvas = document.querySelector('#myChart');
const chart2Canvas = document.querySelector('#chart2');
console.log(chart2Canvas)
// Declare variables that will hold the input values
let homeworkHours = 0;
let extraStudyHours = 0;
let hobbiesHours = 0;
let sportsHours = 0;
let socialActivityHours = 0;
let breaksHours = 0;
// Add an event listener to the form to listen for submit events
form.addEventListener('submit', (event) => {
// Prevent the default form submission behavior
event.preventDefault();
// Get the values of the form inputs
const homeworkInput = document.querySelector('#homework');
const extraStudyInput = document.querySelector('#extra-study');
const hobbiesInput = document.querySelector('#hobbies');
const sportsInput = document.querySelector('#sports');
const socialActivityInput = document.querySelector('#social-activity');
const breaksInput = document.querySelector('#breaks');
// Convert the input values to numbers
homeworkHours = parseInt(homeworkInput.value, 10);
extraStudyHours = parseInt(extraStudyInput.value, 10);
hobbiesHours = parseInt(hobbiesInput.value, 10);
sportsHours = parseInt(sportsInput.value, 10);
socialActivityHours = parseInt(socialActivityInput.value, 10);
breaksHours = parseInt(breaksInput.value, 10);
// Calculate the total number of hours spent on activities
const totalHours = homeworkHours + extraStudyHours + hobbiesHours + sportsHours + socialActivityHours + breaksHours;
// Create a chart using the Chart.js library
const chart = new Chart(canvas, {
type: 'pie',
data: {
labels: ['Homework', 'Extra Study', 'Hobbies', 'Sports', 'Social Activities', 'Breaks'],
datasets: [{
data: [homeworkHours, extraStudyHours, hobbiesHours, sportsHours, socialActivityHours, breaksHours],
backgroundColor: ['#FF6384', '#36A2EB', '#FFCE56', '#00FF00', '#0000FF', '#FFFF00'],
hoverBackgroundColor: ['#FF6384', '#36A2EB', '#FFCE56', '#00FF00', '#0000FF', '#FFFF00'],
}],
},
options: {
responsive: false,
maintainAspectRatio: false,
aspectRatio: 1.5/1, // width:height
title: {
display: true,
text: `Total Hours: ${totalHours}`,
},
},
});
const chart2 = new Chart(chart2Canvas, {
type: 'bar',
data: {
labels: ['Homework', 'Extra Study', 'Hobbies', 'Sports', 'Social Activities', 'Breaks'],
datasets: [{
data: [homeworkHours, extraStudyHours, hobbiesHours, sportsHours, socialActivityHours, breaksHours],
backgroundColor: ['#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0', '#E7E9ED', '#F9CB9C']
}]
},
options: {
responsive: false,
maintainAspectRatio: false,
aspectRatio: 1.5/1,
title: {
display: true,
text: 'Hours Spent on Activities'
},
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
}
});
The Homework category for the bar chart keeps showing an undefined category even though it is defined. Not sure what the problem is, however, is it something to do with the scope of the variables? I've ran my code through console.log and there does not seem to be any referencing mistakes.
My HTML code is here in case there is a problem with it.
<canvas id="myChart" width="400" height="400"></canvas>
<canvas id="chart2" width="400" height="400"></canvas>
<script src="dashboard.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
I've tried running the canvas id through console.log on JS to see if there is a referencing mistake. I've tried to think of other reasons as to why it may not work, such as trying to access the elements before the DOM has finished loading, or is it something to do with the global and local variables in my program?

Related

chart.js how do we extract clicked var?

I have a flask app that utilizes chart.js,
live app can viewed here: https://flaskapp.gullp.repl.co/
I'm trying to pull the clicked value from the chart and put it into a variable that resides outside of the chart function.
Here is my chart logic:
var myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: /* {{ labels }}*/ ['Big Home', 'Medium Home', 'Small Home'] ,
datasets: [{
label: 'count per label',
data: /*{{ values }} */ [3,2,1]
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
//below allows you to click the chart and get the respective value. you will pass this value to python
,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);
console.log('you clicked the graph, now the x value is = ' + x_value)
console.log('since you clicked, this is the chart clicked data = ' + JSON.stringify(chart_clicked_data) )
document.querySelectorAll("tr").forEach((tr, i) => {
if(i === 0){
// skip column names
return
}
if(tr.children[0].innerText != x_value){
tr.style.display = 'none';
}else {
tr.style.display = 'table-row';
}
})
}
}
});
Here is my code trying to extract the x-value:
var chart_clicked_data = { 'score' : myChart.options.x_value}
console.log('this is the json chart clicked data that we want to pass back to flask = ' + JSON.stringify(chart_clicked_data) )
seems like this is not doing the trick:
myChart.config.options.x_value
when i check the console after i click the value, its not showing to be picking up.
entire live code here:
https://repl.it/join/rbkobiqi-gullp
Add an variable outside of your chartFunctions, set that variable in the click handler and then use it
let xVal = ''
var options = {
type: 'bar',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
borderWidth: 1
},
{
label: '# of Points',
data: [7, 11, 5, 8, 3, 7],
borderWidth: 1
}
]
},
options: {
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);
console.log('you clicked the graph, now the x value is = ' + x_value)
xVal = x_value
},
scales: {
yAxes: [{
ticks: {
reverse: false
}
}]
}
}
}
document.getElementById('temp').addEventListener('click', () => {
console.log('X val: ', xVal);
alert('X val: ' + xVal)
})
var ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
canvas {
background-color: #eee;
}
<body>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<button id="temp">
show pressed X value
</button>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.js" integrity="sha512-hZf9Qhp3rlDJBvAKvmiG+goaaKRZA6LKUO35oK6EsM0/kjPK32Yw7URqrq3Q+Nvbbt8Usss+IekL7CRn83dYmw==" crossorigin="anonymous"></script>
</body>

What kind of graph could I use to achieve this with ChartJS (or similar)?

I'm trying to make a deciBel-frequency chart like this in javascript:
[X axis is frequency domain (red, blue and yellow are 4G bands), Y axis is power in dB]
However, the classic bar chart that I find in every library cannot fix the bottom of the bars below 0. I'm trying to find another kind of chart that I could use to achieve this. Orange color is the noise floor power.
Thank you in advance.
No way to create "range" only by one value.
For example, the data for the red bar in your example is not only 20 -or- -180 but -180 to 20 = nested array (Multidimensional Array)
data = [[-180,20]];
snippet:
labels1 = ["a","b","c","d"];
data = [[20,-180],[40,-160],[20,-120]];
var data = {
labels: labels1,
datasets: [
{
label: "hello",
data: data,
backgroundColor: ["yellow", "blue", "orange"],
borderWidth: 5
}
]
}
var options = {
responsive: true,
scales: {
xAxes: [{
stacked: false,
}],
yAxes: [{
stacked: false,
ticks: {
gridLines: {
drawOnChartArea: true
},
max: 100,
min: -180,
}
}]
},
title: {
display: true,
text: name
},
tooltips: {
mode: 'index',
intersect: false,
},
};
/*for(let i = 0; i<10; i++)
{
let labels2 = [];
let datos2 = [];
labels2.push(i);
datos2.push(-120);
}*/
var ctx = document.getElementById("myChart");
var chartInstance = new Chart(ctx, {
type: 'bar',
data: data,
options:options
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<h2>
Hello World!
</h2>
<canvas id='myChart'/>

attempting to destroy previous graph on canvas

I am creating multiple graphs on the same canvas but I am unable to successfully use the destroy() API to clean up the previous data.
HERE IS MY JS CODE FOR CREATING A CHART
const getCountryDataByMonth = async (country) => {
document.getElementById('casesGraphHeader').innerHTML = "Loading....";
const response = await fetch ('https://cors-anywhere.herokuapp.com/https://pomber.github.io/covid19/timeseries.json');
const data = await response.json();
const reports = await data[country];
var i;
var dateList = [];
var caseByDay = [];
var deathsByDay = [];
for(i = 0; i < reports.length; i++){
dateList.push(reports[i].date);
caseByDay.push(reports[i].confirmed);
deathsByDay.push(reports[i].deaths);
}
//GRAPH FOR TOTAL CASES
var casesOptions = {
type: 'bar',
data: {
labels: dateList,
datasets: [
{
label: 'Total Cases',
data: caseByDay,
backgroundColor: '#f49d12',
borderColor: '#f49d12',
fill: false,
borderWidth: 2
}
]
},
options: {
legend: {
labels: {
fontSize: 15
}
},
scales: {
yAxes: [{
ticks: {
reverse: false,
fontSize: 15
}
}],
xAxes: [{
ticks: {
fontSize: 15
}
}],
}
}
}
var totalCasesChart = document.getElementById('totalCasesContainer').getContext('2d');
new Chart(totalCasesChart, casesOptions);
document.getElementById('casesGraphHeader').innerHTML = "Total Cases for "+country;
//GRAPH FOR TOTAL Deaths
var deathOptions = {
type: 'bar',
data: {
labels: dateList,
datasets: [
{
label: 'Total Deaths',
data: deathsByDay,
backgroundColor: '#e84c3d',
borderColor: '#e84c3d',
fill: false,
borderWidth: 2
}
]
},
options: {
legend: {
labels: {
fontSize: 15
}
},
scales: {
yAxes: [{
ticks: {
reverse: false,
fontSize: 15
}
}],
xAxes: [{
ticks: {
fontSize: 15
}
}],
}
}
}
var totalCasesChart = document.getElementById('totalDeathsContainer').getContext('2d');
new Chart(totalDeathsContainer, deathOptions);
document.getElementById('deathsGraphHeader').innerHTML = "Total Deaths for "+country;
};
function renderChart(){
getCountryDataByMonth(document.getElementById('myInput').value);
}
function defaultChart() {
getCountryDataByMonth('US');
}
window.onload = defaultChart;
This is what I tried. I basically did
if(caseBar){
caseBar.destroy();
}
However, this does not work. In my FIDDLE you can try to type China first click to create the graph and then type Italy. Then HOVER over the Italy graph and you will see the stats from china appear on the graph.
Your code is riddle with issues, here is some of the stuff I see:
Look at what you are doing when you create the new charts:
var totalCasesChart = document.getElementById('totalCasesContainer').getContext('2d');
var caseBar = new Chart(totalCasesChart, casesOptions);
document.getElementById('casesGraphHeader').innerHTML = "Total Cases for " + country;
vs
var totalCasesChart = document.getElementById('totalDeathsContainer').getContext('2d');
new Chart(totalDeathsContainer, deathOptions);
document.getElementById('deathsGraphHeader').innerHTML = "Total Deaths for " + country;
You are calling the:
await fetch('https://cors-anywhere.herokuapp.com/https://pomber.github.io/...');
again and again when you should do it just once...
There are many variables that should be global to reduce what you do in getCountryDataByMonth, a perfect example are the totalCasesChart and caseBar
I made a few tweaks to your code here:
https://raw.githack.com/heldersepu/hs-scripts/master/HTML/chart_test.html

How to show slice value inside of slice in pie chart using chart.js

I am having an scenario, where I need to show the slice value inside of the slice in pie chart. I am having more than 1 pie charts on my webpage. Below code is perfectly working but only for 1st Pie Chart and for others its throwing error as below, could you please help me to sort this out?
Error:: unCaught TypeError: cannot read the property 'data' of undefined.
Code::
options:{
animation:{
onComplete:function(){
var ctx = this.chart.ctx;
var dataset_obj = this.data.datasets;
for (var i=0;i<dataset_obj.length; i++){
var meta_obj = dataset_obj[i]._meta[i].data;
for (var j=0; j<meta_obj.length; j++){
var model =meta_obj[j]._model;
start_angle= model.startAngle;
end_angle = model.endAngle;
mid_angle =start_angle + ( end_angle -start_angle)/2;
mid_radius = model.innerRadius + (model.outerRadius -model.innerRadius)/2;
var x =mid_radius*math.cos(mid_angle);
var y = mid_radius*math.cos(mid_angle);
ctx.fillstyle='#fff';
if (dataset_obj[i].data[j] != 0 && meta_obj[j].hidden != true){
ctx.fillText(dataset_obj[i].data[j], model.x+x, model.y+y);
}
}
}
}
}
}
This answers the issue contained in the question's title:
How to show slice value inside of slice in pie chart using chart.js
The code snippet below show how to display the values inside the slices with the use of chartjs-plugin-labels. The code of the samples was extracted from the chartjs-plugin-labels demo page.
var canvas = document.getElementById('myChart');
new Chart(canvas, {
type: 'pie',
data: {
labels: ['January', 'February', 'March'],
datasets: [{
data: [50445, 33655, 15900],
backgroundColor: ['#FF6384', '#36A2EB','#FFCE56']
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
plugins: {
labels: {
render: 'value',
fontColor: ['green', 'white', 'red']
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/emn178/chartjs-plugin-labels/src/chartjs-plugin-labels.js"></script>
<canvas id="myChart"></canvas>
If you want to display the percentage of each slice, you can do the following:
var canvas = document.getElementById('myChart');
new Chart(canvas, {
type: 'pie',
data: {
labels: ['January', 'February', 'March'],
datasets: [{
data: [50445, 33655, 15900],
backgroundColor: ['#FF6384', '#36A2EB','#FFCE56']
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
plugins: {
labels: {
render: 'percentage',
fontColor: ['green', 'white', 'red'],
precision: 2
}
},
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/emn178/chartjs-plugin-labels/src/chartjs-plugin-labels.js"></script>
<canvas id="myChart"></canvas>

Hide Y-axis labels when data is not displayed in Chart.js

I have a Chart.js bar graph displaying two sets of data: Total SQL Queries and Slow SQL Queries. I have Y-axis labels for each respective set of data. The graph can be seen below:
When I toggle one of the sets of data to not display, the corresponding Y-axis labels still display. When interpreting the graph, this is a bit confusing. As seen below:
My question: How can I hide the Y-axis labels of any set of data that is currently not being displayed?
This is how I currently have my chart set up:
<canvas id="SQLPerformanceChart" minHeight="400"></canvas>
<script type="text/javascript">
...
var data = {
labels: labelArray,
datasets: [{
label: "Total SQL Queries",
fill: false,
borderWidth: 1,
borderColor: "green",
backgroundColor: "rgba(0, 255, 0, 0.3)",
yAxisID: "y-axis-0",
data: totalQueriesArray
}, {
label: "Slow SQL Queries",
fill: false,
borderWidth: 1,
borderColor: "orange",
backgroundColor: "rgba(255, 255, 0, 0.3)",
yAxisID: "y-axis-1",
data: slowQueriesArray,
}]
};
var options = {
animation: false,
scales: {
yAxes: [{
position: "left",
ticks: {
beginAtZero: true
},
scaleLabel: {
display: true,
labelString: 'Total SQL Queries'
},
id: "y-axis-0"
}, {
position: "right",
ticks: {
beginAtZero: true
},
scaleLabel: {
display: true,
labelString: 'Slow SQL Queries'
},
id: "y-axis-1"
}]
},
tooltips: {
enabled: true,
mode: 'single',
callbacks: {
title: function(tooltipItem, data) {
return data.label;
},
beforeLabel: function(tooltipItem, data) {
if (tooltipItem.index == 24) {
return data.labels[tooltipItem.index] + " - Now";
} else {
return data.labels[tooltipItem.index] + " - " + data.labels[(tooltipItem.index) + 1];
}
}
}
}
}
var ctx = document.getElementById("SQLPerformanceChart");
var SQLPerformanceChart = new Chart(ctx, {
type: 'bar',
data: data,
options: options
});
</script>
You can add a callback function to legends onClick:
var options = {
animation: false,
scales: {
yAxes: [{
position: "left",
ticks: {
beginAtZero: true
},
scaleLabel: {
display: true,
labelString: 'Total SQL Queries'
},
id: "y-axis-0"
}, {
position: "right",
ticks: {
beginAtZero: true
},
scaleLabel: {
display: true,
labelString: 'Slow SQL Queries'
},
id: "y-axis-1"
}]
},
legend: {
onClick: function(event, legendItem) {
//get the index of the clicked legend
var index = legendItem.datasetIndex;
//toggle chosen dataset's visibility
SQLPerformanceChart.data.datasets[index].hidden =
!SQLPerformanceChart.data.datasets[index].hidden;
//toggle the related labels' visibility
SQLPerformanceChart.options.scales.yAxes[index].display =
!SQLPerformanceChart.options.scales.yAxes[index].display;
SQLPerformanceChart.update();
}
}
}
This solution applies if you are using angular-chartjs, and if you want to apply this behaviour to all displayed charts.
If you want to skip to the code, check this fiddlejs.
You can also check this other fiddlejs to check the default Angular-Chartjs behaviour.
Step by step:
I use the first chart example in angular-chart.js, so this will be the final result after clicking:
<div ng-app="app" ng-controller="MainController as mainCtrl">
<canvas id="line" class="chart chart-line" chart-data="data"
chart-labels="labels" chart-series="series" chart-options="options"
chart-dataset-override="datasetOverride" chart-click="onClick">
</canvas>
</div>
Replace the handler of the global Chart:
Chart.defaults.global.legend.onClick = function (e, legendItem) {
var idx = legendItem.datasetIndex;
// IMPORTANT charts will be created in the second and third step
var chart = charts[e.srcElement.id];
chart.options.scales.yAxes[idx].display = !chart.options.scales.yAxes[idx].display;
var meta = chart.getDatasetMeta(idx);
// See controller.isDatasetVisible comment
meta.hidden = meta.hidden === null ? !chart.data.datasets[idx].hidden : null;
chart.update();
};
Create a global variable charts so we can get access each of the charts with the canvas id:
var charts = {};
Fill up the charts variables using the chart-create event:
angular.module("app", ["chart.js"]).controller("MainController", function ($scope) {
$scope.$on('chart-create', function (event, chart) {
charts[chart.chart.canvas.id] = chart;
});
$scope.labels = ["January", "February", "March", "April", "May", "June", "July"];
$scope.series = ['Series A', 'Series B'];
$scope.data = [...
I wish there would be a better way of getting a chart from the canvas id, but as far as I know this is the suggested way by the developers.
This solution applies if you are using ng2-charts with chart.js and Angular 7^ and if you want to apply this behavior to all displayed charts.
import Chart from chart.js
Chart.defaults.global.legend.onClick = function (e: MouseEvent, chartLegendLabelItem: ChartLegendLabelItem) {
const idx: number = chartLegendLabelItem.datasetIndex;
const chart = this.chart;
chart.options.scales.yAxes[idx].display = !chart.options.scales.yAxes[idx].display;
const meta = chart.getDatasetMeta(idx);
meta.hidden = meta.hidden === null ? !chart.data.datasets[idx].hidden : null;
chart.update();
};
or for local configuration
legend: <ChartLegendOptions>{
onClick: function (e: MouseEvent, chartLegendLabelItem:ChartLegendLabelItem) {
const idx: number = chartLegendLabelItem.datasetIndex;
const chart = this.chart;
chart.options.scales.yAxes[idx].display =
!chart.options.scales.yAxes[idx].display;
const meta = chart.getDatasetMeta(idx);
meta.hidden = meta.hidden === null ?
!chart.data.datasets[idx].hidden : null;
chart.update();
}
}
I came along this problem using v3.8.0, none of the obove worked for me.
This code works for me.
Note I'm storing all my chart instances in a Map because I have multiple charts on the same page.
var instances = new Map();
When createing the incances I put them there.
and now the hiding of the y axis label and data on legend click:
onClick: function (event, legendItem) {
var instance = instances.get(event.chart.id);
var meta = instance.getDatasetMeta(legendItem.datasetIndex);
var newValue = !meta.yScale.options.display;
meta.hidden = meta.yScale.options.display;
meta.yScale.options.display = newValue;
instance.update();
}

Categories