ChartJS - Line chart issue with only 1 point - javascript

I just found this little bug when I wanted to show 1 single point using line chart.. I don't know why it didn't show the point. Here is the screenshot:
Here is how I created my object:
avg_payment = {
labels: ["Jan"]
datasets: [
{
label: "Average_payment"
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]
}
]
}
This is my current workaround, eventhough it still gives me the same result:
if avg_payment.labels.length is 1
max_val = Math.max(avg_payment.datasets[0].data)
opt = {
scaleOverride : true
scaleSteps : 2
scaleStepWidth : 1
scaleStartValue : max_val - 1
}
myLineChart = new Chart(ctx1).Line(avg_payment, opt)
Is there any workaround for this issue ?

This issues was caused by a variable becoming infinity when chartjs is trying to draw the x axis. The fix for this has to go into the core of Chartjs's scale so you could either extend scale like below or I have added this fix to my custom build of chartjs https://github.com/leighquince/Chart.js
Chart.Scale = Chart.Scale.extend({
calculateX: function(index) {
//check to ensure data is in chart otherwise we will get infinity
if (!(this.valuesCount)) {
return 0;
}
var isRotated = (this.xLabelRotation > 0),
// innerWidth = (this.offsetGridLines) ? this.width - offsetLeft - this.padding : this.width - (offsetLeft + halfLabelWidth * 2) - this.padding,
innerWidth = this.width - (this.xScalePaddingLeft + this.xScalePaddingRight),
//if we only have one data point take nothing off the count otherwise we get infinity
valueWidth = innerWidth / (this.valuesCount - ((this.offsetGridLines) || this.valuesCount === 1 ? 0 : 1)),
valueOffset = (valueWidth * index) + this.xScalePaddingLeft;
if (this.offsetGridLines) {
valueOffset += (valueWidth / 2);
}
return Math.round(valueOffset);
},
});
var line_chart_data = {
labels: ["Jan"],
datasets: [{
label: "Average_payment",
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]
}]
};
var ctx = $("#line-chart").get(0).getContext("2d");
var lineChart = new Chart(ctx).Line(line_chart_data);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<script src="https://rawgit.com/nnnick/Chart.js/master/Chart.min.js"></script>
<canvas id="line-chart" width="100" height="100"></canvas>

Related

Is there a way in a donut Chart.JS to show a % out of 100

I have a donut chart showing a number. However is there a way which the donut chart can be out of 100%, so the number I give in the data section: say 21, will show 21% complete out of 100?
Image to show desired outcome:
So the donut ring is greyed out and the coloured section is how much has been completed (or what number we allocate to the data section, I've been looking at the documentation and cannot see a way this can be done?
Code I have currently:
<canvas id="Chart1" style="width=5 height=5"></canvas>
<?php
$var = 3;
?>
var ctx = document.getElementById('Chart1').getContext('2d');
var chart = new Chart(ctx, {
// The type of chart we want to create
type: 'doughnut',
// The data for our dataset
data: {
labels: ['% Complete'],
datasets: [{
label: 'chart1',
backgroundColor: 'rgb(102, 178, 255)',
borderColor: 'rgb(102, 178, 255)',
// Below I just pull out 1 number from the db
data: [<?php echo $var ?>]
}]
},
});
My code outputs the below (so the 3 fills up the whole donut), whereas I would like it show 3% out of 100% complete.
Try passing data of [3, 97]. You're trying to use it as a loading indicator but it seems designed for showing 100% of things broken into parts.
If you pass simply [3], then that's 100% of your dataset
Create a two value dataset, like:
[percent_value, 100-percent_value]
Here's a full demo:
const originalDoughnutDraw = Chart.controllers.doughnut.prototype.draw;
Chart.helpers.extend(Chart.controllers.doughnut.prototype, {
draw: function() {
const chart = this.chart;
const {
width,
height,
ctx,
config
} = chart.chart;
const {
datasets
} = config.data;
const dataset = datasets[0];
const datasetData = dataset.data;
const completed = datasetData[0];
const text = `${completed}% completed`;
let x, y, mid;
originalDoughnutDraw.apply(this, arguments);
const fontSize = (height / 350).toFixed(2);
ctx.font = fontSize + "em Lato, sans-serif";
ctx.textBaseline = "top";
x = Math.round((width - ctx.measureText(text).width) / 2);
y = (height / 1.8) - fontSize;
ctx.fillStyle = "#000000"
ctx.fillText(text, x, y);
mid = x + ctx.measureText(text).width / 2;
}
});
var context = document.getElementById('myChart').getContext('2d');
var percent_value = 3;
var chart = new Chart(context, {
type: 'doughnut',
data: {
labels: ['Completed', 'Pending'],
datasets: [{
label: 'First dataset',
data: [percent_value, 100 - percent_value],
backgroundColor: ['#00baa6', '#ededed']
}]
},
options: {}
});
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.8.0"></script>
<canvas id="myChart"></canvas>

Trying to update background color on chart.js

can someone help me with fixing the colors of my chart, it seems that it does not accepting the details included on it
HTML
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
</head>
<body>
<h1>Live Updating Chart.js</h1>
<canvas id="myChart" width="800" height="700"></canvas>
</body>
</html>
JS
$(document).ready(function(){
var count = 10;
var data = {
labels : ["1","2","3","4","5", "6", "7", "8", "9", "10"],
datasets : [
{
// backgroundColor: '#8bd600',
// fillColor : "rgba(220,220,220,0.5)",
// strokeColor : "rgba(220,220,220,1)",
// pointColor : "rgba(220,220,220,1)",
// pointStrokeColor : "#fff",
data : [0]
},
{
backgroundColor: '#8bd600',
pointBackgroundColor: '#8bd600',
borderWidth: 1,
borderColor: '#ffffff',
data : [28,48,40,19,96,87,66,97,92,85]
}
]
}
// this is ugly, don't judge me
var updateData = function(oldData){
var labels = oldData["labels"];
var dataSetA = oldData["datasets"][0]["data"];
var dataSetB = oldData["datasets"][1]["data"];
labels.shift();
count++;
labels.push(count.toString());
var newDataA = dataSetA[9] + (20 - Math.floor(Math.random() * (41)));
var newDataB = dataSetB[9] + (20 - Math.floor(Math.random() * (41)));
dataSetA.push(newDataA);
dataSetB.push(newDataB);
dataSetA.shift();
dataSetB.shift();
};
var optionsAnimation = {
//Boolean - If we want to override with a hard coded scale
scaleOverride : false,
//** Required if scaleOverride is true **
//Number - The number of steps in a hard coded scale
scaleSteps : 20,
//Number - The value jump in the hard coded scale
scaleStepWidth : 10,
//Number - The scale starting value
scaleStartValue : 0
}
// Not sure why the scaleOverride isn't working...
var optionsNoAnimation = {
animation : false,
//Boolean - If we want to override with a hard coded scale
scaleOverride : true,
//** Required if scaleOverride is true **
//Number - The number of steps in a hard coded scale
scaleSteps : 1,
//Number - The value jump in the hard coded scale
scaleStepWidth : 1,
//Number - The scale starting value
scaleStartValue : 0
}
//Get the context of the canvas element we want to select
var ctx = document.getElementById("myChart").getContext("2d");
var optionsNoAnimation = {animation : false}
var myNewChart = new Chart(ctx);
myNewChart.Line(data, optionsAnimation);
setInterval(function(){
updateData(data);
myNewChart.Line(data, optionsNoAnimation)
;}, 750
);
});
I am trying to get this design
the problem occurs on the background color not accepting the black
You could check the running version of it here https://codepen.io/thisisasample001/pen/NgKKJm
Since, there is no native way of changing the background color of chart in ChartJS, hence, you would have to do some css styling to accomplish so ...
#myChart {
background-color: #22293d;
}
additionally, if you want the graph­'s fill color to match chart­'s background color then, you need to set datasetFill property to false in your chart options ...
var optionsNoAnimation = {
datasetFill: false,
...
}
Here is the working demo on JSFiddle

How can I force the ".0" portion of a value to explicitly display (Chart.JS)?

I have got this data for my chart:
datasets: [
{
label: "Price Compliant",
backgroundColor: "rgba(34,139,34,0.5)",
hoverBackgroundColor: "rgba(34,139,34,1)",
data: [99.0, 99.2, 99.4, 98.9, 99.1, 99.5, 99.6, 99.2, 99.7]
},
{
label: "Non-Compliant",
backgroundColor: "rgba(255, 0, 0, 0.5)",
hoverBackgroundColor: "rgba(255, 0, 0, 1)",
data: [1.0, 0.8, 0.6, 1.1, 0.9, 0.5, 0.4, 0.8, 0.3]
}
]
...which looks like so:
As you can see, the first data point (99.0) displays as 99, truncating the ".0" portion.
There is, of course, some logic to this, but the GUI nazis want that ".0" to be retained.
What do I need to do to force display of even "meaningless" portions of data?
UPDATE
afterDraw() event, for Jaromanda X:
Chart.pluginService.register({
afterDraw: function (chartInstance) {
if (chartInstance.id !== 1) return; // affect this one only
var ctx = chartInstance.chart.ctx;
// render the value of the chart above the bar
ctx.font = Chart.helpers.fontString(14, 'bold', Chart.defaults.global.defaultFontFamily);
ctx.textAlign = 'center';
ctx.textBaseline = 'bottom';
chartInstance.data.datasets.forEach(function (dataset) {
for (var i = 0; i < dataset.data.length; i++) {
var model = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._model;
ctx.fillText(dataset.data[i] + "%", model.base + 180, model.y + 6);
//ctx.fillText(dataset.data[i], model.base + 20, model.y + 6);
}
});
}
});
As mentioned by #machineghost in his comment, this is a known issue.
But you have still several workarounds to make it work :
Simply change your data into string : (fiddle link)
For example you data will be like this :
{
label: "Price Compliant",
backgroundColor: "rgba(34,139,34,0.5)",
hoverBackgroundColor: "rgba(34,139,34,1)",
data: ["99.0", "99.2", "99.4", "98.9", "99.1", "99.5", "99.6", "99.2", "99.7"]
}
Chart.js will handle it as a classic string and then won't remove the ".0".
Add the ".0" in your code, using plugins : (fiddle link)
A small condition (using ternaries) can make it easy to write & read :
ctx.fillText(dataset.data[i] + (Number.isInteger(dataset.data[i]) ? ".0" : "") + "%", ((model.x + model.base) / 2 ), model.y + (model.height / 3));
It is basically checking if the value is an integer (no decimals) and add a ".0" in the string if it is.
Both codes will give you the following result :

Drawing line chart in chart.js placing dots only when value changes

I am using a chart in my page using chart.js I have on my x axis dates while on my y axis values(cost). I want to keep the line chart continue its value until there is a change and have coded for that. Here is the output
In this as marked if the value is same I have dots plotted. I have an option to remove all dots but I want to remove dots if the value is same as previous.(there is no change). I would like to know if this is doable. If please guide me how to go for it?
Its not the same as marked for being duplicate...
I want them to be true or flse based on value. If value is zero or same as previous then dont display dot
HERE IS MY CODE
as.dashboard = {};
as.dashboard.adjustWidgetsHeight = function () {
var maxHeight = 0;
$(".panel-widget .panel-heading").height('auto');
$(".panel-widget .panel-heading").each(function () {
if ($(this).height() > maxHeight) {
maxHeight = $(this).height();
}
});
$(".panel-widget .panel-heading").height(maxHeight);
};
as.dashboard.initChart = function () {
var data = {
labels: dayss,
//Number - Tension of the bezier curve between points
bezierCurveTension : 0.4,
datasets: [
{
label: "Machine costs History",
fillColor: "rgba(151,187,205,0.2)",
strokeColor: "rgba(151,187,205,1)",
pointColor: "rgba(151,187,205,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(151,187,205,1)",
bezierCurve : false,
data: costVariations
}
, {
label: "My third dataset", // This ONE IS DUMMY IT HELPS IN
// SOLVING OVERLAPPING TOOL TIPS
}
]
};
var ctx = document.getElementById("myChart").getContext("2d");
var myLineChart = new Chart(ctx).Line(data, {
responsive: true,
maintainAspectRatio: false,
tooltipTemplate: "<%if (value!=0){%><%= value %> <%= units %> <%}%>",
multiTooltipTemplate: "<%if (value!=0){%><%= value %> <%= units %> <%}%>",
});
};
$(document).ready(function () {
as.dashboard.adjustWidgetsHeight();
as.dashboard.initChart();
});
...
for (var i = 1; i <= data.datasets[0].data.length - 1; i++)
if (data.datasets[0].data[i - 1] === data.datasets[0].data[i])
myChart.datasets[0].points[i].display = false;
where myChart is your chart object
Fiddle - http://jsfiddle.net/3tok57dL/

Chart.js BarChart not appearing

I am trying to get a BarChart appear using MVC, EF and json. For some reason the Bar Chart does not load and Google Chrome shows the following error "Uncaught TypeError: Cannot read property 'length' of undefined" in the console.
I am struggling to find the error in my code and hope someone can help me.
DashboardController
[AjaxOnly]
public ActionResult WeeklyLatenessSummary()
{
ChartHelper chart = new ChartHelper();
DateTime d = DateTime.Today;
int offset = d.DayOfWeek - DayOfWeek.Monday;
offset = (offset < 0) ? 6 : offset;
DateTime startDate = d.AddDays(-offset);
DateTime endDate = startDate.AddDays(7);
var data = (from a in _db.Attendances
join at in _db.AttendanceTypes on a.Type equals at.AttendanceTypeID
where a.Date >= startDate
&& a.Date < endDate
&& at.AttendanceTypeCode == "L"
group at by at.AttendanceTypeDescription into g
select new
{
value = g.Count()
}).ToList();
return Json(JsonConvert.SerializeObject(chart.PopulateBarChart("Weekly Lateness", data)), JsonRequestBehavior.AllowGet);
}
JSON Result
"[{\"label\":\"Weekly Lateness\",\"data\":\"3\",\"fillColor\":\"#F7464A\",\"strokeColor\":\"#F7464A\",\"highlightFill\":\"#FF5A5E\",\"highlightStroke\":\"#FF5A5E\"}]"
View
<div class="col-md-12">
<div class=" panel panel-success">
<div class="panel-heading">
<h3 class="panel-title">Weekly Lateness Summary</h3>
</div><!--End of panel-heading-->
<div class="panel-body">
<canvas id="weekly-lateness" width="650" height="300" class="center-block"></canvas>
</div><!--End of panel-body-->
</div><!--End of panel-->
</div><!--End of col-md-12-->
smChart.js
var ctx3 = $("#weekly-lateness").get(0).getContext("2d");
var barChartOptions = {
//Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value
scaleBeginAtZero: true,
//Boolean - Whether grid lines are shown across the chart
scaleShowGridLines: true,
//String - Colour of the grid lines
scaleGridLineColor: "rgba(0,0,0,.05)",
//Number - Width of the grid lines
scaleGridLineWidth: 1,
//Boolean - Whether to show horizontal lines (except X axis)
scaleShowHorizontalLines: true,
//Boolean - Whether to show vertical lines (except Y axis)
scaleShowVerticalLines: true,
//Boolean - If there is a stroke on each bar
barShowStroke: true,
//Number - Pixel width of the bar stroke
barStrokeWidth: 2,
//Number - Spacing between each of the X value sets
barValueSpacing: 5,
//Number - Spacing between data sets within X values
barDatasetSpacing: 1,
//String - A legend template
legendTemplate: "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].fillColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>"
}
var getDaysInMonth = function (month, year) {
return new Date(year, month, 0).getDate();
}
var dates = [];
for (var i = 1; i <= getDaysInMonth(8,2015); i++) {
dates.push(i);
}
window.onload = function () {
$.ajax({
type: "GET",
dataType: "json",
url: "/Dashboard/WeeklyLatenessSummary/",
success: function (data) {
var json = JSON.parse(data);
var chartData = [];
for (var k in json) {
chartData.push(json[k])
}
if (chartData.length == 0) {
$('#weekly-lateness').hide();
$('#weekly-lateness').before("<h3 class='text-center'>No Data</h3>");
}
else {
var myPieChart3 = new Chart(ctx3).Bar(chartData, barChartOptions)
}
}
});
}
You're building the chartData variable incorrectly. For reference, here is what it should look like (from http://www.chartjs.org/docs/#bar-chart)
{
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]
}
] };
The labels (not to be confused with label which is the series name)correspond to the x axis entries and the there is a datasets element for each series. Each series has as many entries in its data array as there are entries in labels
With the sample JSON you've given, here's one way to make the data appear
var chartData = {
labels: ['Potatoes'],
datasets: []
};
for (var k in json) {
chartData.datasets.push(json[k])
}
It will be easier to change it based on your requirements about what needs to be displayed and on which dimension.
I would venture to say it is bombing on your legend template when it is rendered.
datasets is undefined
legendTemplate: "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].fillColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>"

Categories