Related
I am new to generating a chart using the ajax mechanism and chartjs. It seems that I managed to generate the graph but they are plotted incorrectly. Please enlighten me on how I could improve. Would really appreciate Thank you
Following is my code in javascript to plot chart
var ctx = document.getElementById('myChart').getContext('2d');
$.ajax({
url:"/dashboard",
type:"POST",
data: {},
error: function() {
alert("Error");
},
success: function(data, status, xhr) {
debugger
// declare all variables to draw chart
var chartDim = {};
var chartDim = data.chartDim;
var xLabels = data.labels;
var vLabels = [];
var vData = [];
let newValues =[]
// get from database to push to draw the chart
for (const [key, values] of Object.entries(chartDim)) {
vLabels.push(key);
let newValues = values.map(myFunction);
debugger;
vData.push(newValues);
}
debugger
var myChart = new Chart(ctx, {
data: {
labels: xLabels,
datasets: []
},
options: {
responsive: false
}
});
// draw to chart
debugger
for (i= 0; i < vLabels.length; i++ ) {
myChart.data.datasets.push({
label: vLabels[i],
type: "bar",
// borderColor: '#'+(0x1ff0000+Math.random()*0xffffff).toString(16).substr(1,6),
borderColor: '#'+(0x1100000+Math.random()*0xffffff).toString(16).substr(1,6),
backgroundColor: "rgba(249, 238, 236, 0.74)",
data: vData[i],
spanGaps: true
});
myChart.update();
}
}})
This happens because while making your chart you push each data point as a new dataset. Instead you just need to use a single dataset and pass the data array to it like so:
var myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: xLabels,
datasets: [{
borderColor: '#' + (0x1100000 + Math.random() * 0xffffff).toString(16).substr(1, 6),
backgroundColor: "rgba(249, 238, 236, 0.74)",
data: vData,
spanGaps: true
}]
},
options: {
responsive: false
}
});
I'm trying to show weight_id retrieved from mysql data in a chart.js tooltip (shown as (weight_ids[index]) in the image). And later, I intend to show a modal instead of a tooltip to let users update or delete that data. I presume I cannot achieve that without linking the linechart's point data with id stored in mysql. How can I incorporate this id data?
I would appreciate any help very much.
enter image description here
My code is as follows:
<canvas id="myChart"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.9.4/dist/Chart.min.js"></script>
{{-- グラフを描画--}}
<script>
//ラベル
const labels = #json($date_labels);
// id
const weight_ids = #json($weight_ids);
//体重ログ
const weight_logs = #json($weight_logs);
const aryMax = function(a, b) {
return Math.max(a, b);
};
const aryMin = function(a, b) {
return Math.min(a, b);
};
let min_label = Math.floor((weight_logs).reduce(aryMin) - 0.5);
let max_label = Math.ceil((weight_logs).reduce(aryMax) + 0.5);
console.log(weight_ids);
console.log(weight_logs);
console.log(min_label, max_label);
//グラフを描画
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
type: 'line',
data : {
labels: labels, // x軸ラベル
datasets: [
{
label: `Weight (weight_ids[index])`,
data: weight_logs,
tension: 0,
borderColor: "rgba(37,78,255,1)",
backgroundColor: "rgba(0,0,0,0)",
pointRadius: 3
}
]
},
options: {
title: {
display: false,
text: ''
},
legend: {
display: false,
},
scales: {
yAxes: [
{
ticks: {
min: min_label, // ラベル最小値
max: max_label, // ラベル最大値
},
scaleLabel: {
display: true,
fontSize: 16,
labelString: '体重 (kg)'
}
}
],
},
hover: {
mode: 'point'
},
onClick: function clickHandler(evt) {
var firstPoint = myChart.getElementAtEvent(evt)[0];
if (firstPoint) {
var label = myChart.data.labels[firstPoint._index];
var value = myChart.data.datasets[firstPoint._datasetIndex].data[firstPoint._index];
console.log(label);
console.log(value);
if (value) {
$('#weidhtModal').modal('show');
}
}
}
}
});
</script>
Thank you!
I found a way to retrieve weight_id using the following function.
onClick: function clickHandler(evt, activeElements) {
if (activeElements.length) {
var element = this.getElementAtEvent(evt);
var index = element[0]._index;
var _datasetIndex = element[0]._datasetIndex;
var weightId = weight_ids[index];
var weightLog = weight_logs[index];
console.log(index);
console.log(weightId);
console.log(this.data.labels[index]);
console.log(weightLog);
}
}
I have more doughnut charts in one page, every chart have different data but I show the same tooltips. It's tooltips of the last chart. I don't found the problem.
This is js code:
var idList = [];
// --------------------
// Memo ID list...
// --------------------
jQuery('.idKQI').each(function (i) {
idList.push( jQuery(this).text() );
});
var idListLength = idList.length;
// ------------------
// Doughnut loop...
// ------------------
var dataDoughnutChart = {
labels: [
'KO',
'OK'
],
datasets: [{
data: [30,70],
backgroundColor: [
'#DF0101', // red
'#31B404' // green
]
}]
};
var optionsDoughnutChart = {
maintainAspectRatio: false,
responsive: true,
legend: {
display: false
},
tooltips: {
mode: 'dataset'
}
};
var idLav;
var ctxDoughnut;
var labelAdd;
var errati;
var esatti;
for (var i = 0, max = idListLength; i < max; i++) {
idLav = '#' + idList[i] + "_chartDoughnut";
ctxDoughnut = jQuery(idLav);
labelAdd = jQuery(idLav).data("labeladd");
errati = jQuery(idLav).data("errati");
esatti = jQuery(idLav).data("esatti");
dataDoughnutChart.labels[0] = 'Errati' + labelAdd;
dataDoughnutChart.labels[1] = 'Esatti' + labelAdd;
dataDoughnutChart.datasets[0].data[0] = errati;
dataDoughnutChart.datasets[0].data[1] = esatti;
var myDoughnutChart = new Chart(ctxDoughnut, {
type: 'doughnut',
data: dataDoughnutChart,
options: optionsDoughnutChart
});
}
If I have two doughnut chart, in witch in the first chart datasets.data = [10,90], and in the second chart datasets.data = [2,98], the tooltips for all two chart show 'Errati: 2' and 'Esatti: 98'. It's values like the second chart.
I'm also try to use an array for var dataDoughnutChart like:
var myDoughnutChart = new Chart(ctxDoughnut, {
type: 'doughnut',
data: dataDoughnutChart[i],
options: optionsDoughnutChart
});
but don't resolve.
Thanks for your help.
Resolved like below:
// .....
configDoughnutList.push(
{
type: 'doughnut',
data: {
datasets: [{
data: [
30,
70
],
backgroundColor: [
'#DF0101', // rosso
'#31B404' // verde
],
label: 'daughnut_163'
}],
labels: [
'Errati',
'Corretti'
]
},
options: {
responsive: true,
legend: {
display: false
},
animation: {
animateScale: true,
animateRotate: true
},
tooltips: {
mode: 'dataset'
}
}
}
);
configDoughnutList[i].data.datasets[0].data[0] = errati;
configDoughnutList[i].data.datasets[0].data[1] = esatti;
configDoughnutList[i].data.labels[0] = 'Errati' + labelAdd;
configDoughnutList[i].data.labels[1] = 'Esatti' + labelAdd;
window.myDoughnut = new Chart(ctxDoughnut, configDoughnutList[i]);
I'm quite new with JS, and have a problem with Chart.js and multiple charts.
I load all libraries like jquery and chart.js from CDNs
Then I have one plugins.js for localy loaded plugins
And one js called main.js with all the custom JS.
I get this error:
Uncaught TypeError: Cannot read property 'style' of undefined
My question is:
How do I keep all my Chart.js charts in one .js file, that get loaded on all website pages, but not all pages contains all charts.
(I have a site that consists on multiple pages with different charts, and I'm thinking it's smarter to load all at once in one JS-file)
Appreciate any other pointers also. :)
-
About the code
I've posted the code below and also created a JSFiddle: https://jsfiddle.net/NoLooseEnds/1pt3m41a/
In the main.js I have all my Chart.js data like so (among other things):
// *************************************
// Chart.js
// *************************************
// Global Settings
Chart.defaults.global.defaultFontColor = "rgba(43,43,43,1)";
Chart.defaults.global.defaultFontFamily = "'ApexNew-Medium', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif";
Chart.defaults.global.defaultFontSize = 12;
Chart.defaults.global.defaultFontStyle = "normal";
Chart.defaults.global.maintainAspectRatio = false;
// Disable click on legend
Chart.defaults.global.legend.onClick = (e) => e.stopPropagation();
// Bar Charts settings
var barChart__options = {
};
// Doughnut settings
var dnutChart__options = {
legend: {
position: "bottom",
// Disable click on legend
onClick: (e) => e.stopPropagation()
}
};
// *************************************
// Datasets
// *************************************
// Bar Chart X
var barChartX__data = {
labels: ["Alpha", "Bravo", "Charlie"],
datasets: [
{
label: "2015",
borderWidth: 0,
backgroundColor: "rgba(43,43,43,1)",
data: [410,430,110]
},
{
label: "2016",
borderWidth: 0,
backgroundColor: "rgba(233,131,0,1.0)",
data: [405,360,150]
}
]
};
const barChartX = $("#barChartX");
let ChartX = new Chart(barChartX, {
type: "bar",
data: barChartX__data,
options: barChart__options
});
// Doughnut Chart Y
var dnutChartY__data = {
labels: ["Alpha", "Bravo"],
datasets: [
{
label: "Points",
borderWidth: 0,
backgroundColor: ["rgba(43,43,43,1)", "rgba(233,131,0,1.0)"],
data: [90,270]
}
]
};
const dnutChartY = $("#dnutChartY");
let ChartY = new Chart(dnutChartY, {
type: "doughnut",
data: dnutChartY__data,
options: dnutChart__options
});
// Doughnut Chart Z
var dnutChartZ__data = {
labels: ["Alpha", "Bravo", "Charlie"],
datasets: [
{
label: "Points",
borderWidth: 0,
backgroundColor: ["rgba(233,131,0,1.0)","rgba(239,162,64,1.0)","rgba(244,193,127,1.0)"],
data: [405,360,150]
}
]
};
const dnutChartZ = $("#dnutChartZ");
let ChartZ = new Chart(dnutChartZ, {
type: "doughnut",
data: dnutChartZ__data,
options: dnutChart__options
});
And my HTML like so:
<div class="example__chart">
<div>
<canvas id="barChartX" height="400" width="400"></canvas>
</div>
<!--
<div>
<canvas id="dnutChartY" height="400" width="400"></canvas>
</div>
-->
<div>
<canvas id="dnutChartZ" height="400" width="400"></canvas>
</div>
</div>
As you can see I have commented out a part to trigger the error.
You can create a helper function and check whenever the DOM element exists for the barChartX, dnutChartY, dnutChartZ or any other DOM element like this:
var doChart = function(o, d) {
if (typeof(o) != 'undefined' && o.length > 0) {
return new Chart(o, d);
} else {
return null;
}
}
Then your Chart.js code will be like this
// *************************************
// Chart.js
// *************************************
// Global Settings
Chart.defaults.global.defaultFontColor = "rgba(43,43,43,1)";
Chart.defaults.global.defaultFontFamily = "'ApexNew-Medium', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif";
Chart.defaults.global.defaultFontSize = 12;
Chart.defaults.global.defaultFontStyle = "normal";
Chart.defaults.global.maintainAspectRatio = false;
// Disable click on legend
Chart.defaults.global.legend.onClick = (e) => e.stopPropagation();
var doChart = function(o, d) {
if (typeof(o) != 'undefined' && o.length > 0) {
return new Chart(o, d);
} else {
return null;
}
}
// Bar Charts settings
var barChart__options = {};
// Doughnut settings
var dnutChart__options = {
legend: {
position: "bottom",
// Disable click on legend
onClick: (e) => e.stopPropagation()
}
};
// *************************************
// Datasets
// *************************************
// Bar Chart X
var barChartX__data = {
labels: ["Alpha", "Bravo", "Charlie"],
datasets: [{
label: "2015",
borderWidth: 0,
backgroundColor: "rgba(43,43,43,1)",
data: [410, 430, 110]
},
{
label: "2016",
borderWidth: 0,
backgroundColor: "rgba(233,131,0,1.0)",
data: [405, 360, 150]
}
]
};
const barChartX = $("#barChartX");
/*let ChartX = new Chart(barChartX, {
type: "bar",
data: barChartX__data,
options: barChart__options
});*/
let ChartX = doChart(barChartX, {
type: "bar",
data: barChartX__data,
options: barChart__options
});
// Doughnut Chart Y
var dnutChartY__data = {
labels: ["Alpha", "Bravo"],
datasets: [{
label: "Points",
borderWidth: 0,
backgroundColor: ["rgba(43,43,43,1)", "rgba(233,131,0,1.0)"],
data: [90, 270]
}]
};
const dnutChartY = $("#dnutChartY");
let ChartY = doChart(dnutChartY, {
type: "doughnut",
data: dnutChartY__data,
options: dnutChart__options
});
// Doughnut Chart Z
var dnutChartZ__data = {
labels: ["Alpha", "Bravo", "Charlie"],
datasets: [{
label: "Points",
borderWidth: 0,
backgroundColor: ["rgba(233,131,0,1.0)", "rgba(239,162,64,1.0)", "rgba(244,193,127,1.0)"],
data: [405, 360, 150]
}]
};
const dnutChartZ = $("#dnutChartZ");
let ChartZ = doChart(dnutChartZ, {
type: "doughnut",
data: dnutChartZ__data,
options: dnutChart__options
});
Please check my working example with no javascript errors here: http://zikro.gr/dbg/html/chartsjs/
UPDATE
Please have a look at the JSFiddle example here: https://jsfiddle.net/h1dafqjx/2/
UPDATE 2
Problem analysis and solution
The problem you are facing, is that you want to have multiple declarations and chart setups in one javascript file that you are going to use on many location which may happen some of these locations do not have some of the declared elements, thus you want your code to handle those cases.
You declare a chart like this:
const barChartX = $("#barChartX");
let ChartX = new Chart(barChartX, {
type: "bar",
data: barChartX__data,
options: barChart__options
});
In order to handle a missing DOM element, like when the $("#barChartX") does not exists, you need to create a condition to check those cases.
For excample:
const barChartX = $("#barChartX");
if(typeof(barChartX) != 'undefined' && barChartX.length > 0) {
let ChartX = new Chart(barChartX, {
type: "bar",
data: barChartX__data,
options: barChart__options
});
}
With the obove code, you check if the barChartX is defined and if it actually contains any elements with the barChartX.length > 0 condition.
Now, in order to do not repeat things, we keep it simple and do all that checking in a function. Thus we create the function doChart like this:
var doChart = function(o, d) {
if (typeof(o) != 'undefined' && o.length > 0) {
return new Chart(o, d);
} else {
return null;
}
}
The first parameter o is the DOM element we want to check and the second parameter d is the chart object properties.
For example:
let ChartX = doChart(barChartX, {
type: "bar",
data: barChartX__data,
options: barChart__options
});
Here we pass to the doChart function the barChartX jQuery object of the element in order for the function to check if exists. Then we pass the chart object parameters by the second parameter named d, which is the chart object properties for creating the new chart:
{
type: "bar",
data: barChartX__data,
options: barChart__options
}
jsfiddle here.
First time using Chart.js and I cannot find an example with the whole code to review. I have the following data for 3 months to chart:
Project hours billed, Project hours not billed (stacked bar, for each month)
Billed Amount, To Be Billed amount (stacked bar for each month)
I want to stack the hours and also the billing totals, and want two Y axis, one for hours, the other for dollars.
I have got as far as this code, but it does not stack the hours or the invoiced amounts for each month. Also, I cannot seem to format either axis and the values in the labels to time and currency.
Is there an example you can point me to showing this. Thanks!
var barChartData = {
labels: ["January", "February", "March"],
datasets: [{
type: 'bar',
label: 'Billed Hours',
backgroundColor: "rgba(220,220,220,0.5)",
yAxisID: "y-axis-1",
data: [33.56, 68.45, 79.35]
}, {
type: 'bar',
label: 'Non Billed Hours',
backgroundColor: "rgba(222,220,220,0.5)",
yAxisID: "y-axis-1",
data: [3.50, 8.58, 7.53]
}, {
type: 'bar',
label: 'Income',
backgroundColor: "rgba(151,187,205,0.5)",
yAxisID: "y-axis-2",
data: [3800.00, 7565.65, 8500.96]
}, {
type: 'bar',
label: 'Income',
backgroundColor: "rgba(155,187,205,0.5)",
yAxisID: "y-axis-2",
data: [320.00, 780.65, 850.96]
}]
};
var ctx = document.getElementById("projectHours").getContext("2d");
window.myBar = new Chart(ctx, {
type: 'bar',
data: barChartData,
options: {
responsive: true,
hoverMode: 'label',
hoverAnimationDuration: 400,
stacked: true,
title: {
display: true,
text: "Billed / Billable Project Summary"
},
scales: {
yAxes: [{
type: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
display: true,
position: "left",
id: "y-axis-1",
}, {
type: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
display: true,
position: "right",
id: "y-axis-2",
gridLines: {
drawOnChartArea: false
},
}],
},
animation: {
onComplete: function () {
var ctx = this.chart.ctx;
ctx.textAlign = "center";
Chart.helpers.each(this.data.datasets.forEach(function (dataset) {
Chart.helpers.each(dataset.metaData.forEach(function (bar, index) {
ctx.fillText(dataset.data[index], bar._model.x, bar._model.y - 10);
}),this)
}),this);
}
}
}
});
You can extend the bar chart to do this
Preview
Script
Chart.defaults.groupableBar = Chart.helpers.clone(Chart.defaults.bar);
var helpers = Chart.helpers;
Chart.controllers.groupableBar = Chart.controllers.bar.extend({
calculateBarX: function (index, datasetIndex) {
// position the bars based on the stack index
var stackIndex = this.getMeta().stackIndex;
return Chart.controllers.bar.prototype.calculateBarX.apply(this, [index, stackIndex]);
},
// hide preceding datasets in groups other than the one we are in
hideOtherStacks: function (datasetIndex) {
var meta = this.getMeta();
var stackIndex = meta.stackIndex;
this.hiddens = [];
for (var i = 0; i < datasetIndex; i++) {
var dsMeta = this.chart.getDatasetMeta(i);
if (dsMeta.stackIndex !== stackIndex) {
this.hiddens.push(dsMeta.hidden);
dsMeta.hidden = true;
}
}
},
// reverse hideOtherStacks
unhideOtherStacks: function (datasetIndex) {
var meta = this.getMeta();
var stackIndex = meta.stackIndex;
for (var i = 0; i < datasetIndex; i++) {
var dsMeta = this.chart.getDatasetMeta(i);
if (dsMeta.stackIndex !== stackIndex) {
dsMeta.hidden = this.hiddens.unshift();
}
}
},
// we hide preceding datasets in groups other than the one we are in
// we then rely on the normal stacked logic to do its magic
calculateBarY: function (index, datasetIndex) {
this.hideOtherStacks(datasetIndex);
var barY = Chart.controllers.bar.prototype.calculateBarY.apply(this, [index, datasetIndex]);
this.unhideOtherStacks(datasetIndex);
return barY;
},
// similar to calculateBarY
calculateBarBase: function (datasetIndex, index) {
this.hideOtherStacks(datasetIndex);
var barBase = Chart.controllers.bar.prototype.calculateBarBase.apply(this, [datasetIndex, index]);
this.unhideOtherStacks(datasetIndex);
return barBase;
},
getBarCount: function () {
var stacks = [];
// put the stack index in the dataset meta
Chart.helpers.each(this.chart.data.datasets, function (dataset, datasetIndex) {
var meta = this.chart.getDatasetMeta(datasetIndex);
if (meta.bar && this.chart.isDatasetVisible(datasetIndex)) {
var stackIndex = stacks.indexOf(dataset.stack);
if (stackIndex === -1) {
stackIndex = stacks.length;
stacks.push(dataset.stack);
}
meta.stackIndex = stackIndex;
}
}, this);
this.getMeta().stacks = stacks;
return stacks.length;
},
});
and then
...
type: 'groupableBar',
options: {
scales: {
yAxes: [{
ticks: {
// we have to set this manually (or we could calculate it from our input data)
max: 160,
},
stacked: true,
}]
}
}
});
Note that we don't have any logic to set the y axis limits, we just hard code it. If you leave it unspecified, you'll end up with the limits you get if all the bars were stacked in one group.
Fiddle - http://jsfiddle.net/4rjge8sk/