Related
I am displaying a bar chart that has 3 different pieces of information, (project name, number of days remaining, and the end date.) I am displaying the project name on one axis, and the number of days remaining determines the height of the bar. Currently, when I hover over a bar the tooltip displays the information already on the x and y axis. I want it to instead have the end date.
ie: project "b" will end in 2 days (August 4th), when I hover over the bar I want the tooltip to say "End date of 2022-08-04" instead of "b Work Days Remaining: 2"
My json of the data looks like this:
[{"po_num": "a", "days_rem": 10, "date_end": "2022-08-16"},
{"po_num": "b", "days_rem": 2, "date_end": "2022-08-04"},
{"po_num": "c", "days_rem": 6, "date_end": "2022-08-10"}]
Here is the link of the current graph.
https://i.stack.imgur.com/HefRz.png
Here is an MS paint rendering of what I am trying to do:
https://i.stack.imgur.com/GAT2I.png
The implementation code:
link = "{{{BASE_BACK_URL}}}";
$.getJSON(link, function (data) {
let po_names = [];
let days_rem = [];
for (let i = 0; i < data.length; i++) {
po_names.push(data[i]["po_num"]);
days_rem.push(data[i]["days_rem"]);
}
const ctx = document.getElementById('po-timeline-chart');
const myChart = new Chart(ctx, {
type: 'horizontalBar',
data: {
labels: po_names,
datasets: [{
label: 'Work Days Remaining',
data: days_rem,
backgroundColor: 'rgb(0, 89, 178)'
}],
},
options: {
legend: {
align: "end"
},
scales: {
xAxes: [{
ticks: {
beginAtZero: true
}
}]
}
}
});
});
Solution listed below:
$.getJSON(link, function (data) {
let po_names = [];
let days_rem = [];
for (let i = 0; i < data.length; i++) {
po_names.push(data[i]["po_num"]);
days_rem.push(data[i]["days_rem"]);
}
const ctx = document.getElementById("po-timeline-chart");
const myChart = new Chart(ctx, {
type: "horizontalBar",
data: {
labels: po_names,
datasets: [
{
label: "Work Days Remaining",
data: days_rem,
backgroundColor: "rgb(0, 89, 178)",
},
],
},
options: {
tooltips: {
enabled: true,
callbacks: {
// To change title in tooltip
title: (data) => {
return "This PO will run out on";
},
// To change label in tooltip
label: (data) => {
return date_end[data['index']];
},
},
},
legend: {
align: "end",
},
scales: {
xAxes: [
{
ticks: {
beginAtZero: true,
},
},
],
},
},
});
});
so I'm generating charts with a database and displaying them with JSON script and it works fine but the chart only displays when I click an option value, what I'm trying to do now is set a default value option for when the website opens it displays a default chart if that makes sense, below is my chart.js code.
function renderHtmlChart(){
$(document).ready(function (){
var selection= document.getElementById('YEAR').value;
var link = "https://udon.ads.ntu.ac.uk/web/itec30151/N0773065/new/data.php?YEAR='"+selection+"'";
$.ajax({
url: link,
method: "GET",
success: function(data=this.responseText) {
console.log(data);
var Destination = [];
var Bookings = [];
for(var i in data) {
Destination.push(data[i].Destination);
Bookings.push(data[i].Bookings);
}
createChart(Destination,Bookings,selection)
},
error: function(data) {
console.log(data);
}
});
});
}
function createChart(Destination,Bookings,selection){
var universalOptions = {
maintainAspectRatio: true,
responsive: false,
title: {
display: true,
text: 'Top 5 Flight Bookings'
},
legend: {
display: true,
},
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
},
scaleLabel: {
display: true,
labelString: 'Bookings'
}
}],
xAxes: [{
scaleLabel: {
display: true,
labelString: 'Destinations'
}
}],
}
}
var chartdata = {
labels: Destination,
datasets : [
{
label: selection,
data: Bookings,
backgroundColor: ["#3366cc","#dc3912","#ff9900","#109618","#990099"],
borderWidth: '1',
borderColour: 'grey',
hoverBorderColor: 'black',
fill: false,
pointRadius: 0,
}
]
};
//stop overlap
$('select').on('change',function(){
barGraph.destroy();
});
// this makes legend hidden
var update_caption = function(legend) {
labels[legend.text] = legend.hidden;
var selected = Object.keys(labels).filter(function(key) {
return labels[key];
});
};
//this creates new graph
var ctx = document.getElementById('myChart');
var barGraph = new Chart(ctx, {
type: 'bar',
data: chartdata,
options: universalOptions,
responsive: false,
});
}
hope you have latest version of jquery like:-
<script src="https://code.jquery.com/jquery-3.4.1.js" integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU=" crossorigin="anonymous"></script>
hope this will help
//create renderHtmlChart function
function renderHtmlChart()
{
var selection= document.getElementById('YEAR').value;
var link = "https://udon.ads.ntu.ac.uk/web/itec30151/N0773065/new/data.php?YEAR='"+selection+"'";
$.ajax({
url: link,
method: "GET",
success: function(data=this.responseText)
{
console.log(data);
var Destination = [];
var Bookings = [];
for(var i in data)
{
Destination.push(data[i].Destination);
Bookings.push(data[i].Bookings);
}
createChart(Destination,Bookings,selection)
},
error: function(data)
{
console.log(data);
}
});
}
//create createChart function
function createChart(Destination,Bookings,selection)
{
var universalOptions =
{
maintainAspectRatio: true,
responsive: false,
title:
{
display: true,
text: 'Top 5 Flight Bookings'
},
legend:
{
display: true,
},
scales:
{
yAxes: [{
ticks: {
beginAtZero: true,
},
scaleLabel: {
display: true,
labelString: 'Bookings'
}
}],
xAxes: [{
scaleLabel: {
display: true,
labelString: 'Destinations'
}
}],
}
}
var chartdata = {
labels: Destination,
datasets : [
{
label: selection,
data: Bookings,
backgroundColor: ["#3366cc","#dc3912","#ff9900","#109618","#990099"],
borderWidth: '1',
borderColour: 'grey',
hoverBorderColor: 'black',
fill: false,
pointRadius: 0,
}
]
};
// this makes legend hidden
var update_caption = function(legend) {
labels[legend.text] = legend.hidden;
var selected = Object.keys(labels).filter(function(key) {
return labels[key];
});
};
//this creates new graph
var ctx = document.getElementById('myChart');
var barGraph = new Chart(ctx, {
type: 'bar',
data: chartdata,
options: universalOptions,
responsive: false,
});
}
$(funtion(){
//onload call renderHtmlChart function
renderHtmlChart();
//on select input change call renderHtmlChart function
$('select').on('change',function(){
renderHtmlChart();
});
})
I have graph that I built with the Chart.js library :
Normally, Sshare is represented with two color, red and green. In the legend, however, Sshare displays with just the first color value, red.
How can I get both Sshare colors to show in the legend?
I tried searching for a solution in the Chart.js documentation, but could not find a way to edit the legend properties.
My code:
<script>
// chart colors //BLUE & RED & VERT
var colors = ['#007bff','#dc3545',"#008000"];
var colors_suggested =[];
var labels_in =[];
var mshare_value =[];
var svalues =[];
var data_work_in =[
{ "ID":12, "Les": "AB", "Name": " AB_12", "Mmin": 75, "Sshare": 29},
{ "ID":13, "Les": "BB", "Name": " BB_13", "Mmin": 26.8, "Sshare": 36}
];
for (var i = 0; i < data_work_in.length;i++)
{
labels_in.push(data_work_in[i].Name+";"+data_work_in[i].Mill);
mshare_value.push(data_work_in[i].Mmin);
svalues.push(data_work_in[i].Sshare);
if ( data_work_in[i].Sshare >= data_work_in[i].Mmin)
colors_suggested.push(colors[2]);
else {
colors_suggested.push(colors[1]);
}
}
var chBar = document.getElementById("chBar");
var chartData = {
// Label of Entity
labels: labels_in,
// Value of percent category RTI|| VSM ...
datasets: [{
label: 'Mmin',
data: mshare_value,
backgroundColor: colors[0]
},
{
label: 'Sshare',
data: svalues,
xAxisID:'xAxis1',
backgroundColor: colors_suggested
}]
};
if (chBar) {
// new graph
new Chart(chBar, {
type: 'bar',
data: chartData,
options: {
scales: {
xAxes:[
{
barPercentage: 0.9,
categoryPercentage: 0.7,
id:'xAxis1',
type:"category",
ticks:{
callback:function(label){
var sublabel_x = label.split(";")[0];
var label_p = label.split(";")[1];
return sublabel_x;
}
}
},
{
id:'xAxis2',
type:"category",
gridLines: {
drawOnChartArea: false, // only want the grid lines for one axis to show up
},
ticks:{
callback:function(label){
var sublabel_x = label.split(";")[0];
var label_p = label.split(";")[1];
return label_p;
}
}
}],
yAxes: [{
ticks: {
beginAtZero: false
},
scaleLabel: {
display: true,
labelString: '%'
}
}]
},
legend: {
display: true,
legendText : ['Mmin','Sshare']
}
}
});
}
</script>
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
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/