I am using a regular HTML + js setup and I have JQuery + ChartJS 2.9.3
I am creating a line chart, exactly like the samples.
I am querying a JSON from a server using JQuery's getJSON. I am treating it accordingly, and my final datasets and labels are in the correct form an count (checked via debugging).
When clicking my update button which gets and parse the JSON, the chart disappears, only to reappear on a redraw (example: moving the window) just fine with all my data in the correct way.
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
<canvas id="canvas"></canvas>
<br />
<br />
<input type="number" id="limit" min="3" max="100" value="10" step="1" />
<button type="button" id="update">Update</button>
<script>
const colors = ["#fcba03", "#56fc03", "#56fc03", "#054eeb", "#8f05eb", "#eb05e0", "#05ebeb"]
Object.map = function (o, f, ctx) {
ctx = ctx || this;
var result = {};
Object.keys(o).forEach(function (k) {
result[k] = f.call(ctx, o[k], k, o);
});
return result;
}
var config = {
type: 'line',
data: {
labels: [],
datasets: []
},
options: {
responsive: true,
title: {
display: true,
text: "Qualité de l'air"
},
tooltips: {
mode: 'index',
intersect: false,
},
hover: {
mode: 'nearest',
intersect: true
},
scales: {
x: {
display: true,
scaleLabel: {
display: true,
labelString: 'Time'
}
},
y: {
display: true,
scaleLabel: {
display: true,
labelString: 'Value'
}
}
}
}
};
window.onload = function() {
var ctx = document.getElementById('canvas').getContext('2d');
window.myChart = new Chart(ctx, config);
};
document.getElementById('update').addEventListener('click', updateGraph);
function updateGraph() {
var limit = document.getElementById('limit').value;
$.getJSON("http://localhost/api.php?limit=" + limit, function (obj) {
var labels_obj = Object.map(obj, (value) => { return value.numeric.time; });
var arr = $.map(obj, function (el) { return el });
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
const element = obj[key];
delete element.numeric.time;
}
}
var dataset_names = Object.keys(obj[0].numeric);
var datasets_obj = Object.map(dataset_names, (element, index) => {
var data = Object.values(Object.map(obj, (value) => { return value.numeric[dataset_names[index]]; }));
return {
backgroundColor: colors[index],
borderColor: colors[index],
fill: false,
label: element,
data: data,
};
});
window.myChart.config.data.labels = Object.values(labels_obj);
window.myChart.config.data.datasets = Object.values(datasets_obj);
});
window.myChart.update();
}
</script>
</body>
My JSON looks like this:
{
"0": {
"numeric": {
"NH3": "0.995879",
"CO": "0.333835",
"NO2": "0.68154",
"C3H8": "0.406195",
"C4H10": "0.986352",
"CH4": "0.713178",
"H2": "0.606834",
"time": "2019-11-27 09:52:56"
},
"analog": {
"HCHO": "0.688368",
"MQ2": "0.230614",
"time": "2019-11-27 09:51:00"
}
},
"1": {
"numeric": {
"NH3": "0.406223",
"CO": "0.440095",
"NO2": "0.981807",
"C3H8": "0.588751",
"C4H10": "0.998331",
"CH4": "0.225404",
"H2": "0.132026",
"time": "2019-11-27 09:52:55"
},
"analog": {
"HCHO": "0.482279",
"MQ2": "0.768992",
"time": "2019-11-27 09:50:59"
}
} (etc...)
}
Having tried everything to make that chart not disappear. I also get this js error on update: TypeError: this.getDatasetMeta(...).controller is null chart.js#2.9.3:7:107693
Thanks a lot !
Found the issue:
As getJSON is executed asyncronously, and chart.update() was called after it, the chart was updated, and then it's data was changed, causing the bug.
Make sure the chart.update() call is inside the $.getJSON(){...}.
Related
I am working in Vue. My goal is create dynamic stepsize for timeline chart. Here my
TimelineChart.vue:
<template>
<div class="chart chart-timeline">
<ChartLegend
:items="legend"
:allowFilterCreation="allowFilterCreation"
#highlight="highlighted = $event"
#toggle="toggleDatasetVisibility($event)"
#apply-filter="$emit('apply-filter', $event)"
v-if="legend"/>
<div class="chart-container" v-show="hasData">
<canvas ref="chart"></canvas>
<hr> <hr> <hr> Stepsize Y
<input type="number" id ="ySteps">
<hr> Stepsize X
<input type="number" id ="xSteps">
</div>
<h2 class="subtitle is-5" v-if="!hasData">
No data
</h2>
</div>
</template>
<script>
import _ from "lodash";
import Chart from "chart.js";
import Moment from "moment";
import ChartLegend from "#/components/charts/ChartLegend";
import ChartMixin from "#/components/charts/ChartMixin";
import Deferred from "#/Deferred";
export default {
props: ["data", "multiMode", "labelConverter", "valueConverter", "allowFilterCreation"],
components: { ChartLegend },
mixins: [ChartMixin],
data() {
return {
chart: Deferred.create(),
highlighted: null
};
},
computed: {
timeline() {
return this.data ? _.map(this.data.parameter, it => new Moment(it)) : [];
},
keys() {
return this.data ? _.map(this.data.datasets, it => it.name) : [];
},
labels() {
const converter = this.labelConverter || _.identity;
return _.map(this.keys, converter);
},
datasets() {
return this.data ? _.map(this.data.datasets, it => it.data) : [];
},
datasetCount() {
return this.datasets.length;
},
legend() {
if (!this.multiMode) {
return null;
}
return _.zipWith(this.keys, this.labels, this.palette, (key, text, color) => ({
id: key,
text,
color: color.normal,
hidden: _.includes(this.hiddenDatasets, key)
}));
},
chartColors() {
const highlightIndex = this.highlighted !== null && !_.includes(this.hiddenDatasets, this.highlighted)
? _.indexOf(this.keys, this.highlighted)
: null;
const colors = [];
for (const index of _.keys(this.datasets)) {
const color = this.palette[index];
if (highlightIndex !== null) {
if (index == highlightIndex) {
colors.push({ backgroundColor: color.strongMuted, borderColor: color.strong });
} else {
colors.push({ backgroundColor: color.weakMuted, borderColor: color.weak });
}
} else {
colors.push({ backgroundColor: color.normalMuted, borderColor: color.normal });
}
}
return colors;
}
},
mounted() {
this.chart.resolve(new Chart(this.$refs.chart, {
type: "line",
data: {
datasets: [],
labels: []
},
options: {
maintainAspectRatio: false,
legend: { display: false },
scales: {
xAxes: [{
type: "time",
time: {
isoWeekday: true,
displayFormats: {
hour: "HH:mm",
day: "DD-MM",
week: "DD-MM",
month: "MM-YYYY"
}
},
tooltips: {
},
ticks: { source: "labels",
// stepSize: 2
}
}],
yAxes: [{
ticks: { min: 0, precision: 0,
stepSize: 1
}
}]
},
spanGaps: false,
tooltips: {
callbacks: {
label: item => {
const converter = this.valueConverter || _.identity;
const label = this.multiMode ? (this.labels[item.datasetIndex] + ": ") : "";
return label + converter(this.datasets[item.datasetIndex][item.index]);
}
}
}
}
}));
// method that change stepsize
const ySteps = document.getElementById('ySteps');
ySteps.addEventListener('input', (e) => {
// eslint-disable-next-line no-undef
stepSize(this.$refs.chart, e)
});
function stepSize(chart) {
this.chart.resolve.scales.yAxes.stepSize = ySteps.value;
this.$refs.chart.update()
}
watch: {
data() {
this.resetDatasetVisibility();
}
}
}
</script>
When I alter stepsize nothing happen. In debug mode I have noticed the following pattern:
When I create chart 'this.chart' is not undefined. But when I use function stepSize I have an error, that this.chart is undefined. I have read a documentation about it, but I still do not understand how to connect to instance and change the property in the chart.
this.chart inside stepSize function will not be referring to the chart object inside your data node, because stepSize is not a vue compoenent method, its a javascript function. So this wont be refering to your vue scope.
Since you are already passing your chart object as a parameter to stepSize function as stepSize(this.$refs.chart, e), you can make use of the same inside your stepSize function like
function stepSize(chart) {
chart.resolve.scales.yAxes.stepSize = ySteps.value;
chart.update();
}
I have two issues when i'm trying to update my ChartJS values / datasets, the first is when a new dataset is added to chart datasets it's placed far from other bars (this happens when there are other datasets with zero values in it) in same dataset, another issue is that the data is not updated for same dataset when it's y value is chaged.
Here is what i've tried:
let API = [{
"totpag": 6.5,
"descrpag": "CONTANTI",
"data": "2022-02-15T10:00:00"
},
{
"totpag": 5.5,
"descrpag": "POS MANUALE",
"data": "2022-02-15T10:00:00"
},
{
"totpag": 25,
"descrpag": "ASSEGNI",
"data": "2022-02-15T10:00:00"
}
]
const optionsPagamentiBar = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
},
tooltip: {
mode: 'index',
intersect: 0,
usePointStyle: true,
callbacks: {
label: function(context) {
let y = context.parsed.y;
if (y) {
return context.dataset.label + ": " + "€" + context.parsed.y.toFixed(2).replace(/\d(?=(\d{3})+\.)/g, '$&,').replace(/[,.]/g, m => (m === ',' ? '.' : ','));
}
}
}
}
},
scales: {
y: {
ticks: {
display: true,
beginAtZero: true,
callback: function(value, index, values) {
return "€" + value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".");
}
},
grid: {
drawBorder: false,
zeroLineColor: "transparent",
}
},
x: {
display: 1,
ticks: {
padding: 10,
display: true,
fontSize: 10
},
grid: {
display: false
}
}
}
}
const chartBarPayments = new Chart(document.getElementById("chartBarPayments").getContext('2d'), {
type: 'bar',
data: {
labels: [],
datasets: [{
data: [],
}]
},
options: optionsPagamentiBar
});
let periodo = 'giorno'
function getColorsByLabels(labels) {
//const colorScale = d3.interpolateSinebow;
//const colorRangeInfo = {
// colorStart: 0.2,
// colorEnd: 1,
// useEndAsStart: true,
//};
//let COLORS = interpolateColors(labels.length, colorScale, colorRangeInfo);
let COLORS = ["rgb(167, 3, 213)", "rgb(255, 64, 64)", "rgb(24, 114, 244)", "rgb(34, 0, 97)"]
let backgroundColors = {};
COLORS.forEach((color, index) => {
backgroundColors[labels[index]] = color;
})
return backgroundColors;
}
function pagamentiPerFascia(pagamenti) {
let datasets = [];
let timePeriods = [];
let datasetLabels = [];
let indexedData = {};
pagamenti.forEach((pagamento, i) => {
let date = "";
if (periodo == "anno") {
date = moment(pagamento.data).format("MMM YYYY");
} else if (periodo == "mese") {
date = moment(pagamento.data).format("DD MMM");
} else {
date = moment(pagamento.data).format('HH:mm');
}
if (!timePeriods.includes(date)) {
timePeriods.push(date);
}
if (!datasetLabels.includes(pagamento.descrpag)) {
datasetLabels.push(pagamento.descrpag);
}
indexedData[pagamento.descrpag + date] = pagamento.totpag;
})
let backgroundColors = getColorsByLabels(datasetLabels);
datasetLabels.forEach(label => {
const dataset = {
label,
backgroundColor: backgroundColors[label],
data: [],
skipNull: true,
};
timePeriods.forEach(date => {
dataset.data.push({
x: date,
y: indexedData[label + date] || null
})
})
datasets.push(dataset);
})
updateChart(datasets)
}
function updateChart(datasets) {
chartBarPayments.data = {};
datasets.forEach((dataset) => {
// checking if dataset exists in chart
let chartdataset = chartBarPayments.data.datasets.find((data) => data.label === dataset.label);
if (chartdataset) {
dataset.data.forEach((date) => {
// checking if x value exists in chart dataset
let chartdata = chartdataset.data.find((data) => data.x === date.x);
if (chartdata) {
console.log("if", date.y)
// if x value exists i''m setting it with the new value (even if it's the same)
chartdata.y = date.y || null;
chartBarPayments.update();
} else {
// else pushing a new xy value to data
console.log("else", date.y)
chartdataset.data.push({
x: date.x,
y: date.y || null
})
chartBarPayments.update();
}
})
} else {
// if the dataset does not exists i'm pushing it as a new one
chartBarPayments.data.datasets.push(dataset);
chartBarPayments.update();
}
});
}
pagamentiPerFascia(API)
var longPolling = setInterval(() => {
API[0].totpag = 15;
API.push({
"totpag": 16.5,
"descrpag": "VISA",
"data": "2022-02-15T10:00:00"
});
API.push({
"totpag": 16.5,
"descrpag": "VISA",
"data": "2022-02-15T11:00:00"
});
pagamentiPerFascia(API)
}, 5000)
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js#3.7.0/dist/chart.min.js"></script>
<canvas id="chartBarPayments"></canvas>
How can i update the values and is there a way to hide zero values from the chart?
UPDATE:
Solved the issue with spacing between bars by setting null instead of 0 in empty bars and added skipNull to each dataset.
UPDATE 2:
Solved even the issue with value update by replacing
chartData = date;
with
chartData.y = date.y;
The main issue here is that i have to clear the .data every time which cause animation stuttering even when there is no new data, clearing data is needed as the dataset can change any time with all new Y labels.
I ran into a similar situation and I ended up disabling the animation, which obviously solved the problem, but because it looked a bit less dazzling the customer did not like the solution, in the end I had to utilise a hack.
Not sure if this will work for you, but in my case it did, you do this by using setTimeout to 1 millisecond.
Run the update & then set a timeout for a millisecond & run the update again. If this doesn't work, try setting arbitrary data (instead of emptying the data) & then updating to the correct dataset a millisecond later.
In my instance:
refreshScope( endscope ) {
if(this.current_scope == 'today') {
this.current_scope = `week`; // Opposite (changes data on watcher)
} else {
this.current_scope = `today`; // Opposite again.
}
setTimeout(() => {
this.current_scope = `${endscope}`;
}, 1);
}
There are of course other (better) options you can employ, as outlined by ChartJS' performance page:
https://www.chartjs.org/docs/3.1.1/general/performance.html
And ultimately disabling animation will make it seem accurate - not stutter, albeit less dazzling, and not require hacks.
Disabling animation:
new Chart(ctx, {
type: 'line',
data: data,
options: {
animation: false
}
});
on Bar click event of first chart "config.categoriesChart" gets the console error "chart.js:10403 Uncaught TypeError: Cannot read property 'handleEvent' of undefined"
enter image description here which diplayed as chart.legend.handleEvent(args.event);
The issue happens after destroy the First chart on the bar click Event of first chart.
But if I use $('#how_i_spend_canvas').replaceWith($(''));
its working fine without destroy the chart.
enter image description here
Please let me what is the issue?
All code in document.ready
let Chart = require('chart.js');/
let ChartDataLabels = require('chartjs-plugin-datalabels');
let config = window.MvpFE.globalConfiguration.howISpend;
let dataChart = window.dataHowISpendCharts;
let labels = dataChart.howISpendDataCatgories.map(function (e) {
return e.label;
});
let data = dataChart.howISpendDataCatgories.map(function (e) {
return e.data;
});
//Chart Axis's
let scales = {
x: {
ticks: {
font: {
size: config.size,
},
color: config.dataLabelsColor,
},
},
y: {
display: false,
}
};
//Chart legend
let plugins = {
legend: {
display: false,
},
tooltip: {
enabled: true,
},
};
//Chart Data Labels
let dataLabels = {
color: config.dataLabelsColor,
anchor: 'end',
align: 'top',
offset: 0,
formatter: function (value) {
//Include a dollar sign
return '$' + value.toLocaleString();
},
};
//chart data
let howISpendChartdata = {
labels: labels,
datasets: [{
data: data,
backgroundColor: config.catogriesBackgroundColor,
borderColor: config.catogriesBorderColor,
hoverBackgroundColor: config.unSelectedColor,
hoverBorderColor: config.unSelectedColor,
borderWidth: config.barWidth,
borderRadius: config.barRadius,
borderSkipped: 'false',
datalabels: dataLabels
}]
}
// Category heading label text will be from json data
let categoryLabel = "";
//Array to store the bar background colors.
const barColors = [];
//Code to draw Chart
var ctx = document.getElementById('how_i_spend_canvas').getContext('2d');
config.categoriesChart = new Chart(ctx, {
type: 'bar',
data: howISpendChartdata,
// Chart pulgins & Options
plugins: [ChartDataLabels],
options: {
responsive: true,
maintainAspectRatio: false,
aspectRatio: 2,
plugins: plugins,
scales: scales,
onClick: function (evt, element) {
if (element.length > 0) {
const categoriesChart = config.categoriesChart;
let activeBarIndex = element[0].index;
categoryLabel = categoriesChart.data.labels[activeBarIndex];
// destroy any chart instances that are created
if (categoriesChart instanceof Chart) {
categoriesChart.destroy();
}
//$('#how_i_spend_canvas').replaceWith($('<canvas id="SelectedCategory" height="400px"></canvas>')); //replace current canvas
// Code to draw Chart
config.monthlyChart = new Chart(ctx, {
type: 'bar',
data: howISpendChartdata,
plugins: [ChartDataLabels],
options: {
responsive: true,
maintainAspectRatio: false,
aspectRatio: 2,
plugins: plugins,
scales: scales,
onClick: function (e, activeElements) {
//get the colors for bars
if (activeElements.length > 0) { // check the element is selected
const monthlyChart = config.monthlyChart;
monthlyChart.options.animation.colors = false;
monthlyChart.update();
}
}
}
},
});
config.monthlyChart.render();
}
},
}
}); // document.Ready Ends()
WARNING: This solution makes sense only if you're not using the legend plugin or don't need to handle legend item click event.
In my case I was getting this error even though I disabled legend plugin in chart options, like this:
plugins: {
legend: {
display: false
}
}
The error stopped happening after I filtered events handled by the legend plugin, like this:
plugins: {
legend: {
display: false,
events: [] // this line was the key
},
}
I came accros the same issue, reason for the exception is:
Inside the onclick event if you try to destroy the same chart, chart reference becomes null before the event callback function returns. And that's why exception is thrown.
You can fix this by destroying the chart instance after the event callback is completed i.e. using setTimeout function you can destroy the chart after 100ms or so. you can do it like this:
options: {
onClick: function (evt, element) {
if (element.length > 0) {
const categoriesChart = config.categoriesChart;
let activeBarIndex = element[0].index;
categoryLabel = categoriesChart.data.labels[activeBarIndex];
setTimeout(() => {
// destroy any chart instances that are created
if (categoriesChart instanceof Chart) {
categoriesChart.destroy();
}
//$('#how_i_spend_canvas').replaceWith($('<canvas id="SelectedCategory" height="400px"></canvas>')); //replace current canvas
// Code to draw Chart
config.monthlyChart = new Chart(ctx, {
type: 'bar',
data: howISpendChartdata,
plugins: [ChartDataLabels],
options: {
responsive: true,
maintainAspectRatio: false,
aspectRatio: 2,
plugins: plugins,
scales: scales,
onClick: function (e, activeElements) {
//get the colors for bars
if (activeElements.length > 0) { // check the element is selected
const monthlyChart = config.monthlyChart;
monthlyChart.options.animation.colors = false;
monthlyChart.update();
}
}
}
});
config.monthlyChart.render();
}, 100);
}
}
}
Below is solution for anyone facing similar problem:
options: {
onClick: function (evt, element) {
// get the require data from click event
let chart = Chart.getChart(e.chart.canvas.id);
const points = chart.getElementsAtEventForMode(e, 'nearest', { intersect: true }, true);
if (points.length) {
const firstPoint = points[0];
const elementIndex = firstPoint.index;
const datasetIndex = firstPoint.datasetIndex;
const dataset = chart.data.datasets[datasetIndex];
const datasetFieldLabel = dataset.label;
const itemLabel = chart.data.labels[elementIndex];
const itemValue = dataset.data[elementIndex];
setTimeout(() => {
// destroy the chart
// Render another chart
}, 100);
}
}
}
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 am trying to import and read from a JSON file that gets updated every few minutes by a different process. I need to loop through the values in this JSON files for use with ChartJS.org.
If I keep the JSON data local to the script in a var (var jsonfile={}) the chart works as expected.
What I am struggling to do is import the JSON file from outside the script (it is on the local web server under a different folder).
The JSON file looks exactly the same as data in the var below.
The below works as expected.
<div class="row">
<div class="col-4">
<div class="ca-comms-by-month">
</div>
<script>
var jsonfile = {
"comms_by_month": [
{
"name": "July",
"count": 2130
},
{
"name": "August",
"count": 890
},
{
"name": "September",
"count": 1654
},
{
"name": "October",
"count": 120
}
]
};
var labels = jsonfile.comms_by_month.map(function(e) {
return e.name;
});
var data = jsonfile.comms_by_month.map(function(e) {
return e.count;
});
function createConfig(details, data) {
return {
type: 'line',
data: {
labels: labels,
datasets: [{
label: 'Comms count by month',
steppedLine: details.steppedLine,
data: data,
borderColor: details.color,
fill: true,
}]
},
options: {
responsive: true,
title: {
display: false,
text: details.label,
},
tooltips: {
enabled:true, // Disable this for custom tool tips || http://www.chartjs.org/docs/latest/configuration/tooltip.html
mode: 'index',
intersect: false,
cornerRadius:0
},
hover: {
mode: 'nearest',
intersect: true
},
scales: {
xAxes: [{
display: true,
scaleLabel: {
display: true,
labelString: 'Month'
}
}],
yAxes: [{
display: true,
scaleLabel: {
display: true,
labelString: 'Count'
},
ticks: {
beginAtZero:true
}
}]
},
legend: {
display: false, // False to hide the legdend dataset tile
labels: {
fontColor: 'rgb(255, 99, 132)'
}
}
}
};
}
window.onload = function()
{
var container = document.querySelector('.ca-comms-by-month');
var steppedLineSettings = [{
steppedLine:false,
label: '',
color: window.chartColors.purple
}];
steppedLineSettings.forEach(function(details) {
var div = document.createElement('div');
div.classList.add('chart-container');
var canvas = document.createElement('canvas');
div.appendChild(canvas);
container.appendChild(div);
var ctx = canvas.getContext('2d');
var config = createConfig(details, data);
new Chart(ctx, config);
});
};
</script>
</div>
</div>
The below code will display some of the data in the updated JSON file based on the getElementById names but this is no good to me as I need the ChartJS to go and get the values.
<h1 class="toolsportal text-right">Temp</h1>
<p id="demo"></p>
<p id="demo1"></p>
<br /><br /><br /><br />
<script>
var jsonurl = 'http://mydevicename/portal/js/export_json/dash-comms-month.json';
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function()
{
if (this.readyState == 4 && this.status == 200)
{
var jsonfile = JSON.parse(this.responseText);
document.getElementById("demo").innerHTML = jsonfile.comms_by_month[0].month;
document.getElementById("demo1").innerHTML = jsonfile.comms_by_month[0].name;
}
};
xmlhttp.open("GET", jsonurl, true);
xmlhttp.send();
</script>
What I can't put together is how I can get the values out of the updated JSON file using the below functions that happily go and get the data from the local jsonfile{} var.
var labels = jsonfile.comms_by_month.map(function(e){return e.name;});
var data = jsonfile.comms_by_month.map(function(e){return e.count;});
I am clearly missing something fundamental, any pointers would be great.
Thanks
I have answered my own question by properly looking into what XMLHttpRequest() & JSON.parse() does.
If someone is looking to hook a JSON file into https://www.chartjs.org/ charts then the below might help.
The JSON file
"comms_by_month":[
{
"name": "July",
"month":7,
"count":0
},
{
"name": "August",
"month":8,
"count":1652
},
{
"name": "September",
"month":9,
"count":600
},
{
"name": "October",
"month":10,
"count":0
},
{
"name": "November",
"month":11,
"count":0
},
{
"name": "December",
"month":12,
"count":0
}
]
Get the JSON file into a var
<script>
// Set the var for the json file located on the web server
var jsonFile_dash_comms_by_month = 'http://hostname/portal/js/export_json/dash-comms-by-month.json';
var request = new XMLHttpRequest();
request.open("GET",jsonFile_dash_comms_by_month,false);
request.send(null)
var jsonObj_dash_comms_by_month = JSON.parse(request.responseText);
</script>
A div where the chart will be displayed
<div class="ca-comms-by-month"></div>
Functions to get the labels and datasets into a var
<script>
var labels = jsonObj_dash_comms_by_month.comms_by_month.map(function(e) {
return e.name;
});
var data = jsonObj_dash_comms_by_month.comms_by_month.map(function(e) {
return e.count;
});
</script>
Function to create the CharJS config
See (http://www.chartjs.org/docs/latest/configuration/) for more about ChartJs config.
<script>
function createConfig(details, data) {
return {
type: 'line',
data: {
labels: labels,
datasets: [{
label: 'Comms count by month',
steppedLine: details.steppedLine,
data: data,
borderColor: details.color,
fill: true,
}]
},
options: {
responsive: true,
title: {
display: false,
text: details.label,
},
tooltips: {
enabled:true, // Disable this for custom tool tips || http://www.chartjs.org/docs/latest/configuration/tooltip.html
mode: 'index',
intersect: false,
cornerRadius:0
},
hover: {
mode: 'nearest',
intersect: true
},
scales: {
xAxes: [{
display: true,
scaleLabel: {
display: true,
labelString: 'Month'
}
}],
yAxes: [{
display: true,
scaleLabel: {
display: true,
labelString: 'Count'
},
ticks: {
beginAtZero:true
}
}]
},
legend: {
display: false, // False to hide the legdend dataset tile
labels: {
fontColor: 'rgb(255, 99, 132)'
}
}
}
};
}<script>
On load function to display the chart
<script>
window.onload = function()
{
var container = document.querySelector('.ca-comms-by-month');
var steppedLineSettings = [{
steppedLine:false,
label: '',
color: window.chartColors.purple
}];
steppedLineSettings.forEach(function(details) {
var div = document.createElement('div');
div.classList.add('chart-container');
var canvas = document.createElement('canvas');
div.appendChild(canvas);
container.appendChild(div);
var ctx = canvas.getContext('2d');
var config = createConfig(details, data);
new Chart(ctx, config);
});
};
</script>
This gave me the below chart
I would be keen to hear from anyone who can point out any improvements on the above. I am expecting to have 10+ charts on a dashboard type page.
Thanks
edwoli