Related
chart is displaying but vertical bar is not showing. data is coming from handler but it is not able to assign into variable 'data:data.datasets'. Please help me.*
Model Class
ChartVM.cs
*use for definig the properties for chart displaying *
public class ChartVM
{
public List<string> Labels{get;set;}
public List<DataSetRow> Datasets{get;set;}
}
public class DataSetRow
{
public List<int> Data{get;set;}
public string BackgroundColor { get;set;}
public string Color { get; set; }
}
ChartDemo.cs
public JsonResult OnGetBarChart()
{
var vm = new ChartVM();
vm.Labels = new List<string>();
vm.Datasets = new List<DataSetRow>();
var result = context.CountryPopulations.ToList();
vm.Labels = result.Select(x => x.Name).ToList();
var ds1 = new DataSetRow
{
BackgroundColor = "#f2f2f2",
Color = "#24248f",
Data = result.Select(x => x.Male).ToList()
};
var ds2 = new DataSetRow
{
BackgroundColor = "#f2f2f2",
Color = "#ff0080",
Data = result.Select(x => x.Female).ToList()
};
vm.Datasets.Add(ds1);
vm.Datasets.Add(ds2);
return new JsonResult(vm);
}
ChartDemo.cshtml
HTML code
<div>
<canvas id="myChart" style="max-height:400px; max-width:500px"></canvas>
</div>
javaScript
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/js/chart.min.js" asp-append-version="true"></script>
<script type="text/javascript">
$(document).ready(function () {
BarChart();
});
function BarChart() {
var XaxisTitle = 'Colour Bar';
var Yaxistitle = 'Rupees';
var legendTitle = 'Months';
const ctx = document.getElementById('myChart');
debugger;
$.ajax({
type:'GET',
url:'?handler=BarChart',
data:{},
success:function(data){
new Chart(ctx, {
type: 'bar',
data: {
labels:data.labels,
datasets: [{
label: legendTitle,
data:data.datasets,
borderWidth: 1,
}]
},
options: {
scales: {
x: {
display: true,
beginAtZero: true,
title: {
display: true,
text: XaxisTitle,
color: '#911',
font: {
family: 'Comic Sans MS',
size: 20,
weight: 'bold',
lineHeight: 1.2,
},
padding: { top: 20, left: 0, right: 0, bottom: 0 }
}
},
y: {
display: true,
title: {
display: true,
text: Yaxistitle,
color: '#191',
font: {
family: 'Times',
size: 20,
style: 'normal',
lineHeight: 1.2
},
padding: { top: 30, left: 0, right: 0, bottom: 0 }
}
}
}
}
});
},
error:function(){
alert('Something went wrong!!');
}
});
}
</script>
see output image vertical bar is not showing
but it is not able to assign into variable 'data:data.datasets'
Error is here:
datasets: [{
label: legendTitle,
data:data.datasets,
borderWidth: 1,
}]
Single dataset in chartjs is complex object with data points and options.
So, in sample provided you are trying to pass to data points property (array of primitive integer values) the bunch of datasets.
Quick fix:
data: {
labels:data.labels,
datasets: [...data.datasets]
}
Changed colors and scrapped data from here.
Displaying TOP-10 countries:
public JsonResult OnGetBarChart()
{
var vm = new ChartVM();
vm.Datasets = new List<DataSetRow>();
var result = context.Populations.OrderByDescending(x => x.TotalPop).Take(10).ToList();
vm.Labels = result.Select(x => x.Name).ToList();
var ds1 = new DataSetRow
{
Label = "Male",
BackgroundColor = "#ffA500",
BorderColor = "#ffA500",
Data = result.Select(x => x.MalePop).ToList()
};
var ds2 = new DataSetRow
{
Label = "Female",
BackgroundColor = "#00B612",
BorderColor = "#00B612",
Data = result.Select(x => x.FemalePop).ToList()
};
vm.Datasets.Add(ds1);
vm.Datasets.Add(ds2);
return new JsonResult(vm);
}
I referred to "How to change line segment color of a line graph in Chart.js" and extended the chart to redraw the line segment with a different color.
But after adding plugin "chartjs-plugin-zoom" to support zoom in/out the chart, I could see the line chart is out of axis boundary. See the following code.
https://jsfiddle.net/sd9rx84g/2/
var ctx = document.getElementById('myChart').getContext('2d');
//adding custom chart type
Chart.defaults.multicolorLine = Chart.defaults.line;
Chart.controllers.multicolorLine = Chart.controllers.line.extend({
draw: function(ease) {
var
startIndex = 0,
meta = this.getMeta(),
points = meta.data || [],
colors = this.getDataset().colors,
area = this.chart.chartArea,
originalDatasets = meta.dataset._children
.filter(function(data) {
return !isNaN(data._view.y);
});
function _setColor(newColor, meta) {
meta.dataset._view.borderColor = newColor;
}
if (!colors) {
Chart.controllers.line.prototype.draw.call(this, ease);
return;
}
for (var i = 2; i <= colors.length; i++) {
if (colors[i-1] !== colors[i]) {
_setColor(colors[i-1], meta);
meta.dataset._children = originalDatasets.slice(startIndex, i);
meta.dataset.draw();
startIndex = i - 1;
}
}
meta.dataset._children = originalDatasets.slice(startIndex);
meta.dataset.draw();
meta.dataset._children = originalDatasets;
points.forEach(function(point) {
point.draw(area);
});
}
});
var chart = new Chart(ctx, {
// The type of chart we want to create
type: 'multicolorLine',
// The data for our dataset
data: {
labels: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"],
datasets: [{
label: "My First dataset",
borderColor: 'rgb(255, 99, 132)',
data: [35, 10, 5, 2, 20, 30, 45, 0, 10, 5, 2, 20, 30, 45],
//first color is not important
colors: ['', 'red', 'green', 'blue', 'red', 'brown', 'black']
}]
},
// Configuration options go here
options: {
plugins: {
zoom: {
pan: {
// pan options and/or events
enabled: true,
mode: 'xy'
},
zoom: {
enabled: true,
drag: false,
mode: 'x',
speed: 1
}
}
}
}
});
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.9.3"></script>
<script src="https://cdn.jsdelivr.net/npm/hammerjs#2.0.8"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom#0.7.7"></script>
<canvas id="myChart"></canvas>
line chart out of axis boundary
How can I resolve this issue?
Thanks
Forrest
I want to make my bar-chart interactive by allowing the user to click on a slice to drill down. I believe that the way to do this is to create an onclick handler on the canvas, and use getSegmentsAtEvent() to determine which slice was clicked. I see one example in Pie chart works but in bar type it doesn't. Any ideas?
This works in Pie type, but not with Bar type,
Chart.types.Pie.extend({
name: "PieUnique",
addData: function(segment, atIndex, silent) {
var index = atIndex || this.segments.length;
this.segments.splice(index, 0, new this.SegmentArc({
value: segment.value,
outerRadius: (this.options.animateScale) ? 0 : this.outerRadius,
innerRadius: (this.options.animateScale) ? 0 : (this.outerRadius / 100) * this.options.percentageInnerCutout,
fillColor: segment.color,
highlightColor: segment.highlight || segment.color,
showStroke: this.options.segmentShowStroke,
strokeWidth: this.options.segmentStrokeWidth,
strokeColor: this.options.segmentStrokeColor,
startAngle: Math.PI * this.options.startAngle,
circumference: (this.options.animateRotate) ? 0 : this.calculateCircumference(segment.value),
label: segment.label,
//add option passed
id: segment.id
}));
if (!silent) {
this.reflow();
this.update();
}
},
});
var pieData = [{
value: 300,
color: "#F7464A",
highlight: "#FF5A5E",
label: "Red",
id: "1-upi"
}, {
value: 50,
color: "#46BFBD",
highlight: "#5AD3D1",
label: "Green",
id: "2-upi"
}, {
value: 100,
color: "#FDB45C",
highlight: "#FFC870",
label: "Yellow",
id: "3-upi"
}, {
value: 40,
color: "#949FB1",
highlight: "#A8B3C5",
label: "Grey",
id: "4-upi"
}, {
value: 120,
color: "#4D5360",
highlight: "#616774",
label: "Dark Grey",
id: "5-upi"
}];
var ctx = document.getElementById("chart-area").getContext("2d");
window.myPie = new Chart(ctx).PieUnique(pieData);
document.getElementById("chart-area").onclick = function(evt) {
var activePoints = window.myPie.getSegmentsAtEvent(evt);
if (activePoints[0]) {
var label = activePoints[0].label;
var value = activePoints[0].value;
var id = activePoints[0].id;
alert('label = ' + label + ' | value = ' + value + ' | id = ' + id);
}
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/1.1.1/Chart.min.js"></script>
<canvas id="chart-area"></canvas>
I expect to create the same functionality but in Bar type.
First it worth to mention the version that you are using is the old 1.1.
You have a couple of problems here, to change the chart type you will first need to change how you set the pieData variable, after that the getSegmentsAtEvent event only works with the pie type, the proper event is getBarsAtEvent.
Here is an working example (please note that I haven't used the original chart data):
Chart.types.Bar.extend({
name: "PieUnique",
addData: function(segment, atIndex, silent) {
var index = atIndex || this.segments.length;
this.segments.splice(index, 0, new this.SegmentArc({
value: segment.value,
outerRadius: (this.options.animateScale) ? 0 : this.outerRadius,
innerRadius: (this.options.animateScale) ? 0 : (this.outerRadius / 100) * this.options.percentageInnerCutout,
fillColor: segment.color,
highlightColor: segment.highlight || segment.color,
showStroke: this.options.segmentShowStroke,
strokeWidth: this.options.segmentStrokeWidth,
strokeColor: this.options.segmentStrokeColor,
startAngle: Math.PI * this.options.startAngle,
circumference: (this.options.animateRotate) ? 0 : this.calculateCircumference(segment.value),
label: segment.label,
//add option passed
id: segment.id
}));
if (!silent) {
this.reflow();
this.update();
}
},
});
var pieData = {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [{
label: "My First dataset",
fillColor: "rgba(220,220,220,0.5)",
strokeColor: "rgba(220,220,220,0.8)",
highlightFill: "rgba(220,220,220,0.75)",
highlightStroke: "rgba(220,220,220,1)",
data: [65, 59, 80, 81, 56, 55, 40]
},
{
label: "My Second dataset",
fillColor: "rgba(151,187,205,0.5)",
strokeColor: "rgba(151,187,205,0.8)",
highlightFill: "rgba(151,187,205,0.75)",
highlightStroke: "rgba(151,187,205,1)",
data: [28, 48, 40, 19, 86, 27, 90]
}
]
};
var ctx = document.getElementById("chart-area").getContext("2d");
window.myPie = new Chart(ctx).PieUnique(pieData);
document.getElementById("chart-area").onclick = function(evt) {
var activePoints = window.myPie.getBarsAtEvent(evt);
if (activePoints[0]) {
var label = activePoints[0].label;
var value = activePoints[0].value;
var id = activePoints[0].id;
alert('label = ' + label + ' | value = ' + value + ' | id = ' + id);
}
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/1.1.1/Chart.min.js"></script>
<canvas id="chart-area"></canvas>
I am trying to hide data labels generated by the data labels plugin for small screens.
I thought that I could use the onResize property of chartjs and set display to false when the width got small. This is much like the hide labels solution found here.
Unfortunately, I've not been able to get this to work. I have the following CodePen that doesn't work.
var moneyFormat = wNumb({
decimals: 0,
thousand: ',',
prefix: '$',
negativeBefore: '-'
});
var percentFormat = wNumb({
decimals: 0,
suffix: '%',
negativeBefore: '-'
});
/*
* Unregister chartjs-plugins-datalabels - not really necessary for this use case
*/
Chart.plugins.unregister(ChartDataLabels);
var doughnutdata = {
labels: ['Housing',
'Food',
'Transportation',
'Clothing',
'Healthcare',
'Childcare',
'Misc'],
datasets: [
{
backgroundColor: [
'#9B2A00',
'#5B5C90',
'#6B8294',
'#1A6300',
'#BE0000',
'#B8A853',
'#64A856'
],
borderColor: [
'#FFFFFF',
'#FFFFFF',
'#FFFFFF',
'#FFFFFF',
'#FFFFFF',
'#FFFFFF',
'#FFFFFF'
],
data: [88480, 57680, 40050, 18430, 23860, 25840, 17490]
}
]
};
var chartOptions = {
responsive: true,
maintainAspectRatio: true,
legend: {
labels: {
boxWidth: 20
}
},
tooltips: {
callbacks: {
label: function (tooltipItem, data) {
var index = tooltipItem.index;
return data.labels[index] + ': ' + moneyFormat.to(data.datasets[0].data[index]) + '';
}
}
},
plugins: {
datalabels: {
anchor: 'end',
backgroundColor: function (context) {
return context.dataset.backgroundColor;
},
borderColor: 'white',
borderRadius: 25,
borderWidth: 1,
color: 'white',
font: {
size: 10
},
formatter: function (value, pieID) {
var sum = 0;
var dataArr = pieID.chart.data.datasets[0].data;
dataArr.map(function (data) {
sum += data;
});
var percentage = percentFormat.to((value * 100 / sum));
return percentage;
}
}
}
};
var doughnutID = document.getElementById('doughnutchart').getContext('2d');
var pieChart = new Chart(doughnutID, {
plugins: [ChartDataLabels],
type: 'doughnut',
data: doughnutdata,
options: chartOptions,
onResize: function(chart, size) {
var showLabels = (size.width < 500) ? false : true;
chart.options = {
plugins: {
datalabels: {
display: showLabels
}
}
};
}
});
Any ideas concerning what I'm doing wrong (and fixes) would be greatly appreciated.
Responsiveness can be implemented using scriptable options and in your case, you would use a function for the display option that returns false if the chart is smaller than a specific size. (Example):
options: {
plugins: {
datalabels: {
display: function(context) {
return context.chart.width > 500;
}
}
}
}
As usual, as soon as I post a question I come up with an answer. One solution using inline plugin definitions is given at the following CodePen. If you put a browser into developer mode and shrink the window to less than 540 px, the data labels will vanish.
The code is shown below:
"use strict";
/* global Chart */
/* global wNumb */
/* global ChartDataLabels */
/*
* Unregister chartjs-plugins-datalabels - not really necessary for this use case
*/
Chart.plugins.unregister(ChartDataLabels);
var moneyFormat = wNumb({
decimals: 0,
thousand: ",",
prefix: "$",
negativeBefore: "-"
});
var percentFormat = wNumb({
decimals: 0,
suffix: "%",
negativeBefore: "-"
});
var doughnutdata = {
labels: [
"Housing",
"Food",
"Transportation",
"Clothing",
"Healthcare",
"Childcare",
"Misc"
],
datasets: [
{
backgroundColor: [
"#9B2A00",
"#5B5C90",
"#6B8294",
"#1A6300",
"#BE0000",
"#B8A853",
"#64A856"
],
borderColor: [
"#FFFFFF",
"#FFFFFF",
"#FFFFFF",
"#FFFFFF",
"#FFFFFF",
"#FFFFFF",
"#FFFFFF"
],
data: [88480, 57680, 40050, 18430, 23860, 25840, 17490]
}
]
};
var chartOptions = {
responsive: true,
maintainAspectRatio: true,
legend: {
labels: {
boxWidth: 20
}
},
tooltips: {
callbacks: {
label: function(tooltipItem, data) {
var index = tooltipItem.index;
return (
data.labels[index] +
": " +
moneyFormat.to(data.datasets[0].data[index]) +
""
);
}
}
},
plugins: {
datalabels: {
anchor: "end",
backgroundColor: function(context) {
return context.dataset.backgroundColor;
},
borderColor: "white",
borderRadius: 25,
borderWidth: 1,
color: "white",
font: {
size: 10
},
formatter: function(value, pieID) {
var sum = 0;
var dataArr = pieID.chart.data.datasets[0].data;
dataArr.map(function(data) {
sum += data;
});
var percentage = percentFormat.to(value * 100 / sum);
return percentage;
}
}
}
};
var doughnutID = document.getElementById("doughnutchart").getContext("2d");
var pieChart = new Chart(doughnutID, {
plugins: [
ChartDataLabels,
{
beforeLayout: function(chart) {
var showLabels = (chart.width) > 500 ? true : false;
chart.options.plugins.datalabels.display = showLabels;
}
},
{
onresize: function(chart) {
var showLabels = (chart.width) > 500 ? true : false;
chart.options.plugins.datalabels.display = showLabels;
}
}
],
type: "doughnut",
data: doughnutdata,
options: chartOptions
});
I hope that this is useful.
Is it possible to define a bar chart with a multi level/category axis?
For instance I'd like to display the Region/Province categories like in this Excel chart:
I found this example using multiple xAxes.
xAxes:[
{
id:'xAxis1',
type:"category",
ticks:{
callback:function(label){
var month = label.split(";")[0];
var year = label.split(";")[1];
return month;
}
}
},
{
id:'xAxis2',
type:"category",
gridLines: {
drawOnChartArea: false, // only want the grid lines for one axis to show up
},
ticks:{
callback:function(label){
var month = label.split(";")[0];
var year = label.split(";")[1];
if(month === "February"){
return year;
}else{
return "";
}
}
}
}
]
The problem is it seems that the two axes are not really linked and the alignment of second axis is based on values instead of aligning in middle of lower level category. this case cause issues
Is there a clean way to achieve this in chart.js?
Update:
I ended up creating a feature request on chartjs.
you can provide value separately for different axis via datesets and provide an object with different configuration option (borderColor, pointBackgroundColor, pointBorderColor) etc, i hope It'll help.
here is the link for the with an update (fiddle you shared) Updated Fiddle
data: {
labels: ["January;2015", "February;2015", "March;2015", "January;2016", "February;2016", "March;2016"],
datasets: [{
label: '# of Votes',
xAxisID:'xAxis1',
data: [12, 19, 3, 5, 2, 3]
},
// here i added another data sets for xAxisID 2 and it works just fine
{
label: '# of Potatoes',
xAxisID:'xAxis2',
data: [9, 17, 28, 26, 29, 9]
}]
}
I hope that solves your problem :)
Hope this helps,
I did a bit of research and couldn't find methods to implement your solution in chartjs. Chartjs has grouped bar charts but not subgrouped bar charts like in your case.
Example: http://jsfiddle.net/harshakj89/ax3zxtzw/
Here are some alternatives,
D3js (https://d3js.org/) can be used to create sub grouped bar charts.Data can be loaded from csv or json. D3 is highly configurable, but you may have to put some effort than chartsjs.
https://plnkr.co/edit/qGZ1YuyFZnVtp04bqZki?p=preview
https://stackoverflow.com/questions/37690018/d3-nested-grouped-bar-chart
https://stackoverflow.com/questions/15764698/loading-d3-js-data-from-a-simple-json-string
ZingChart is a commercial tool and can be used to implement bar charts with sub groupes.
https://www.zingchart.com/docs/chart-types/bar-charts/
But I prefer D3 over this library. because D3 comes under BSD License.
This should work as per your requirement http://tobiasahlin.com/blog/chartjs-charts-to-get-you-started/#8-grouped-bar-chart
The best library I could found to have exactly this feature is Highcharts, this is my implementation:
and here http://jsfiddle.net/fieldsure/Lr5sjh5x/2/ you can find out how to implement it.
$(function () {
var chart = new Highcharts.Chart({
chart: {
renderTo: "container",
type: "column",
borderWidth: 5,
borderColor: '#e8eaeb',
borderRadius: 0,
backgroundColor: '#f7f7f7'
},
title: {
style: {
'fontSize': '1em'
},
useHTML: true,
x: -27,
y: 8,
text: '<span class="chart-title"> Grouped Categories with 2 Series<span class="chart-href"> Black Label </span> <span class="chart-subtitle">plugin by </span></span>'
},
yAxis: [{ // Primary yAxis
labels: {
format: '${value}',
style: {
color: Highcharts.getOptions().colors[0]
}
},
title: {
text: 'Daily Tickets',
style: {
color: Highcharts.getOptions().colors[0]
}
}
}, { // Secondary yAxis
title: {
text: 'Invoices',
style: {
color: Highcharts.getOptions().colors[0]
}
},
labels: {
format: '${value}',
style: {
color: Highcharts.getOptions().colors[0]
}
},
opposite: true
}]
,
series: [{
name: 'Daily',
type: 'column',
yAxis: 1,
data: [4, 14, 18, 5, 6, 5, 14, 15, 18],
tooltip: {
valueSuffix: ' mm'
}
}, {
name: 'Invoices',
type: 'column',
data: [4, 17, 18, 8, 9, 5, 13, 15, 18],
tooltip: {
valueSuffix: ' °C'
}
}],
xAxis: {
categories: [{
name: "1/1/2014",
categories: ["Vendor 1", "Vendor 2", "Vendor 3"]
}, {
name: "1/2/2014",
categories: ["Vendor 1", "Vendor 2", "Vendor 3"]
}, {
name: "1/3/2014",
categories: ["Vendor 1", "Vendor 2", "Vendor 3"]
}]
}
});
});
body {
padding: 0px !important;
margin: 8px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="http://code.highcharts.com/highcharts.js"></script>
<script src="http://blacklabel.github.io/grouped_categories/grouped-categories.js"></script>
<div id="container" class="chart-container"></div>
But the problem is the library is not free for commercial purposes, and this is the Chartjs implementation, in my case it is look like this:
const data ={"labels":[{"label":"Exams","children":["Wellness Examination"]},{"label":"Surgery","children":["Neuter Surgery"]},{"label":"Vaccines","children":["Bordetella"]},{"label":"Dentistry","children":["Dental Cleaning"]},{"label":"Diagnostics","children":["Other","Pre-Anesthetic","Adult Diagnostics","Pre-Anesthetic Diagnostics","Heartworm & Tick Borne Disease Test"]},{"label":"Treatments/Other","children":["Other","Microchip"]}],"datasets":[{"label":"Consumed","backgroundColor":"red","tree":[{"value":0,"children":["0"]},{"value":0,"children":["0"]},{"value":1,"children":["1"]},{"value":0,"children":["0"]},{"value":15,"children":["0","1","3","11","0"]},{"value":15,"children":["2","13"]}]},{"label":"Purchased","backgroundColor":"blue","tree":[{"value":28,"children":["28"]},{"value":1,"children":["1"]},{"value":24,"children":["24"]},{"value":10,"children":["10"]},{"value":103,"children":["2","16","34","49","2"]},{"value":165,"children":["75","90"]}]}]};
window.onload = () => {
const ctx = document.getElementById("canvas").getContext("2d");
window.myBar = new Chart(ctx, {
type: 'bar',
data: data,
options: {
responsive: true,
title: {
display: true,
text: 'Chart.js Hierarchical Bar Chart'
},
layout: {
padding: {
// add more space at the bottom for the hierarchy
bottom: 45
}
},
scales: {
xAxes: [{
type: 'hierarchical',
stacked: false,
// offset settings, for centering the categorical
//axis in the bar chart case
offset: true,
// grid line settings
gridLines: {
offsetGridLines: true
}
}],
yAxes: [{
stacked: false,
ticks: {
beginAtZero: true
}
}]
}
}
});
};
canvas {
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://unpkg.com/chart.js/dist/Chart.bundle.js"></script>
<script src="https://unpkg.com/chartjs-scale-hierarchical"></script>
<div id="container" style="width: 75%;">
<canvas id="canvas"></canvas>
</div>
for each more column just add another dataset.