Chart.js display no data message using django - javascript

I have a pie chart that I want to display no data message when there is no data to be shown I have tried the methods here but it seems to stop displaying the chart all together even when there is data. currently if there is no data it shows NAN% the data it based off the user data from a model. I have also tried to just use the if statement for the part displaying the Nan% but the if statement also just seems to cause it to stop rendering the chart.
JS:
<script>
{% block jquery %}
var endpoint = '/api/chart1'
var defaultData = []
var labels = []
$.ajax({
methode: "GET",
url: endpoint,
success: function(data){
labels = data.labels
defaultData = data.default
setChart1()
},
error: function(error_data){
console.log("error")
console.log(error_data)
}
})
function setChart1(){
var ctx = document.getElementById('myChart').getContext('2d');
Chart.register(ChartDataLabels);
var myChart = new Chart(ctx, {
type: 'pie',
plugins: [ChartDataLabels],
data: {
labels: labels,
datasets: [{
label: '',
data: defaultData,
backgroundColor: [
'rgba(255, 51, 51, 0.2)',
'rgba(255, 255, 0, 0.2)',
'rgba(0, 204, 204, 0.2)',
],
borderColor: [
'rgba(255, 51, 51, 1)',
'rgba(204, 204, 0, 1)',
'rgba(0, 204, 204, 1)',
],
borderWidth: 1
}]
},
options: {
plugins: {
tooltip: {
enabled: false
},
datalabels: {
anchor: 'center',
align: 'center',
formatter: (value, ctx) => {
let sum = 0;
let dataArr = ctx.chart.data.datasets[0].data;
dataArr.map(data => {
sum += data;
});
let percentage = (value*100 / sum).toFixed(2)+"%";
return percentage;
},
color: '#000',
}
}
}
});
}
{% endblock %}
</script>
HTML:
<div class="card bg-light mb-4">
<div class="card-header">
Assigned Tickets-By Priority
</div>
<div class="card-body">
<canvas id="myChart" width="400" height="400" ></canvas>
</div>
</div>
View:
def get_chart1(request):
highTickets = Issue.objects.filter(Q(priority='High') & Q(assigned_users=request.user)).count()
lowTickets = Issue.objects.filter(Q(priority='Low') & Q(assigned_users=request.user)).count()
medTickets = Issue.objects.filter(Q(priority='Medium') & Q(assigned_users=request.user)).count()
labels = ['High', 'Med', 'Low']
default = [highTickets, medTickets, lowTickets]
data = {
'labels': labels,
'default': default,
}
return JsonResponse(data)

Related

Show line based on drop down selection

I am using D3chart. I have four line charts displayed at a time. I need to add a drop down box. Based on the selection I need to display line chart one by one. Could someone help me on this please?
Dropdown will be on the top so once I select option 1 - line 1 should be displayed, option 2 - line 2 and option 3 - line 3 & so on.
My code is here:
<canvas id="myChart" width="100" height="100"></canvas>
<script>
d3.csv("sample-ec.csv").then(makeChart);
function makeChart(players) {
var playerLabels = players.map(function (d) {
return d.Date;
});
var room1Data = players.map(function (d) {
return d.Room1;
});
var room2Data = players.map(function (d) {
return d.Room2;
});
var room3Data = players.map(function (d) {
return d.Room3;
});
var room4Data = players.map(function (d) {
return d.Room4;
});
var chart = new Chart("myChart", {
type: "line",
aspectRatio: 3,
options: {
scales: {
y: {
beginAtZero: true
}
}
},
data: {
labels: playerLabels,
datasets: [
{
label : 'Room 1',
data: room1Data,
backgroundColor: 'rgba(54, 162, 235, 0)',
borderColor:'rgb(54, 162, 235)'
},
{
label : 'Room 2',
data: room2Data,
backgroundColor: 'rgba(75, 192, 192, 0)',
borderColor:'rgb(75, 192, 192)'
},
{
label : 'Room 3',
data: room3Data,
backgroundColor: 'rgba(153, 102, 255, 0)',
borderColor:'rgb(153, 102, 255)'
},
{
label : 'Room 4',
data: room4Data,
backgroundColor: 'rgba(255, 159, 64, 0)',
borderColor:'rgb(255, 159, 64)'
}
]
}
});
}
</script>
</body>
</html>

How can i get 3rd Axis in Chart js?

Trying to put a 3rd axis to the chart...,
the object data is beeing received, but not displayed. What is wrong ?
I checked that the data is pushed , but it is not displayed.
I have no idea , why data is not in the chart,
the spelling should be ok.
If pushing data from IOT,
I always get 3 Objects with data,
as shown in the Picture.
/* eslint-disable max-classes-per-file */
/* eslint-disable no-restricted-globals */
/* eslint-disable no-undef */
$(document).ready(() => {
// if deployed to a site supporting SSL, use wss://
const protocol = document.location.protocol.startsWith('https') ? 'wss://' : 'ws://';
const webSocket = new WebSocket(protocol + location.host);
// A class for holding the last N points of telemetry for a device
class DeviceData {
constructor(deviceId) {
this.deviceId = deviceId;
this.maxLen = 50;
this.timeData = new Array(this.maxLen);
this.temperatureData = new Array(this.maxLen);
this.PrsInData =new Array(this.maxLen);
this.PrsOutData =new Array(this.maxLen);
}
addData(time,Temperature, PrsIn,PrsOut) {
this.timeData.push(time);
this.temperatureData.push(Temperature);
this.PrsInData.push(PrsIn);
this.PrsOutData.push(PrsOut);
if (this.timeData.length > this.maxLen) {
this.timeData.shift();
this.temperatureData.shift();
this.PrsOutData.shift();
this.PrsInData.shift();
}
}
}
// All the devices in the list (those that have been sending telemetry)
class TrackedDevices {
constructor() {
this.devices = [];
}
// Find a device based on its Id
findDevice(deviceId) {
for (let i = 0; i < this.devices.length; ++i) {
if (this.devices[i].deviceId === deviceId) {
return this.devices[i];
}
}
return undefined;
}
getDevicesCount() {
return this.devices.length;
}
}
const trackedDevices = new TrackedDevices();
// Define the chart axes
const chartData = {
datasets: [
{
fill: false,
label: 'Temperature',
yAxisID: 'Temperature',
borderColor: 'rgba(255, 204, 0, 1)',
pointBoarderColor: 'rgba(255, 204, 0, 1)',
backgroundColor: 'rgba(255, 204, 0, 0.4)',
pointHoverBackgroundColor: 'rgba(255, 204, 0, 1)',
pointHoverBorderColor: 'rgba(255, 204, 0, 1)',
spanGaps: true,
},
{
fill: false,
label: 'PrsIn',
yAxisID: 'PrsIn',
borderColor: 'rgba(24, 120, 240, 1)',
pointBoarderColor: 'rgba(24, 120, 240, 1)',
backgroundColor: 'rgba(24, 120, 240, 0.4)',
pointHoverBackgroundColor: 'rgba(24, 120, 240, 1)',
pointHoverBorderColor: 'rgba(24, 120, 240, 1)',
spanGaps: true,
},
{
fill: false,
label: 'PrsOut',
yAxisID: 'PrsOut',
borderColor: 'rgba(24, 24, 240, 1)',
pointBoarderColor: 'rgba(24, 24, 240, 1)',
backgroundColor: 'rgba(24, 24, 240, 0.4)',
pointHoverBackgroundColor: 'rgba(24, 24, 240, 1)',
pointHoverBorderColor: 'rgba(24, 24, 240, 1)',
spanGaps: true,
}
]
};
const chartOptions = {
scales: {
yAxes: [{
id: 'Temperature',
type: 'linear',
scaleLabel: {
labelString: 'Temperature (ºC)',
display: true,
},
position: 'left',
ticks: {
max: 420,
min: 0
}
},
{
id: 'PrsIn',
type: 'linear',
scaleLabel: {
labelString: 'PrsIn',
display: true,
},
position: 'right',
}
,
{
id: 'PrsOut',
type: 'linear',
scaleLabel: {
labelString: 'PrsOut',
display: true,
},
position: 'right',
} ]
}
};
// Get the context of the canvas element we want to select
const ctx = document.getElementById('iotChart').getContext('2d');
const myLineChart = new Chart(
ctx,
{
type: 'line',
data: chartData,
options: chartOptions,
});
// Manage a list of devices in the UI, and update which device data the chart is showing
// based on selection
let needsAutoSelect = true;
const deviceCount = document.getElementById('deviceCount');
const listOfDevices = document.getElementById('listOfDevices');
function OnSelectionChange() {
const device = trackedDevices.findDevice(listOfDevices[listOfDevices.selectedIndex].text);
chartData.labels = device.timeData;
chartData.datasets[0].data = device.Temperature;
chartData.datasets[1].data = device.PrsInData;
chartData.datasets[2].data = device.PrsOutData;
myLineChart.update();
}
listOfDevices.addEventListener('change', OnSelectionChange, false);
// When a web socket message arrives:
// 1. Unpack it
// 2. Validate it has date/time and temperature
// 3. Find or create a cached device to hold the telemetry data
// 4. Append the telemetry data
// 5. Update the chart UI
webSocket.onmessage = function onMessage(message) {
try {
const messageData = JSON.parse(message.data);
console.log(messageData);
// time and either temperature or humidity are required
if (!messageData.MessageDate || (!messageData.IotData.Temperature && !messageData.IotData.PrsIn && !messageData.IotData.PrsOut )) {
return;
}
// find or add device to list of tracked devices
const existingDeviceData = trackedDevices.findDevice(messageData.DeviceId);
if (existingDeviceData) {
existingDeviceData.addData(messageData.MessageDate, messageData.IotData.Temperature, messageData.IotData.PrsIn, messageData.IotData.PrsOut);
} else {
const newDeviceData = new DeviceData(messageData.DeviceId);
trackedDevices.devices.push(newDeviceData);
const numDevices = trackedDevices.getDevicesCount();
deviceCount.innerText = numDevices === 1 ? `${numDevices} device` : `${numDevices} devices`;
newDeviceData.addData(messageData.MessageDate, messageData.IotData.Temperature, messageData.IotData.PrsIn, messageData.IotData.PrsOut);
// add device to the UI list
const node = document.createElement('option');
const nodeText = document.createTextNode(messageData.DeviceId);
node.appendChild(nodeText);
listOfDevices.appendChild(node);
// if this is the first device being discovered, auto-select it
if (needsAutoSelect) {
needsAutoSelect = false;
listOfDevices.selectedIndex = 0;
OnSelectionChange();
}
}
myLineChart.update();
} catch (err) {
console.error(err);
}
};
});
Temperature is missing in the chart

How add the sizes of the slices in the pie chart (at the top) in Chart.js?

I am starting to learn the chart.js library.
I drew a pie chart (like "pie"). When you hover over the slices of the diagram, a number appears in the pop-up window that sets the size of the sector.
new chart(
document.getElementById('diagram_1').getContext('2d'), {
type: 'pie',
data: {
labels: [
'Завершенная задача',
'Новая задача',
'Ошибка выполнения'
],
datasets: [{
label: '# of Votes',
data: [#successful_tasks, #new_tasks, #error_tasks],
backgroundColor: [
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)',
'rgba(255, 99, 132, 0.2)'
],
borderColor: [
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)',
'rgba(255, 99, 132, 1)'
],
borderWidth: 1
}]
},
options: {
scales: {
y: {
beginAtZero: true
}
},
responsive: false
}
}
)
How can you make this number still displayed at the top, where the sectors are listed (I marked this place with a red circle in the picture)?
I can add the required number to the labels array
...
data: {
labels: [
'Завершенная задача: ' + #successful_tasks,
'Новая задача: ' + #new_tasks,
'Ошибка выполнения: ' + #error_tasks
],
...
But then this number will appear twice in the tooltip
You can use the plugin system for this:
var options = {
type: 'pie',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
backgroundColor: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"]
}]
},
options: {
plugins: {
customNumber: {
textColor: 'red',
xOffset: 10,
yOffset: 0,
font: '24px Comic Sans MS'
}
}
},
plugins: [{
id: 'customNumber',
afterDraw: (chart, args, opts) => {
const hoveredSlice = chart._active[0];
const {
ctx,
chartArea: {
right
}
} = chart;
if (!hoveredSlice) {
return;
}
ctx.font = opts.font || '24px verdana, sans-serif'
ctx.fillStyle = opts.textColor || 'black'
const val = chart.data.datasets[hoveredSlice.datasetIndex].data[hoveredSlice.index];
const meassures = ctx.measureText(val);
const height = ctx.measureText('M').width;
ctx.fillText(val, (right - meassures.width - (opts.xOffset || 0)), height + (opts.yOffset || 0))
}
}]
}
var ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.6.0/chart.js"></script>
</body>
I found the answer. My project is written in CoffeeScript, but I think it would be more useful for the StackOverflow community to post the code in JS.
options: {
legend: {
labels: {
generateLabels: function(chart) {
var data = chart.data;
if (data.labels.length && data.datasets.length) {
return data.labels.map(function(label, i) {
var meta = chart.getDatasetMeta(0);
var ds = data.datasets[0];
var arc = meta.data[i];
var custom = arc && arc.custom || {};
var getValueAtIndexOrDefault = Chart.helpers.getValueAtIndexOrDefault;
var arcOpts = chart.options.elements.arc;
var fill = custom.backgroundColor ? custom.backgroundColor : getValueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor);
var stroke = custom.borderColor ? custom.borderColor : getValueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor);
var bw = custom.borderWidth ? custom.borderWidth : getValueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth);
var value = chart.config.data.datasets[arc._datasetIndex].data[arc._index];
return {
text: label + ": " + value,
fillStyle: fill,
strokeStyle: stroke,
lineWidth: bw,
hidden: isNaN(ds.data[i]) || meta.data[i].hidden,
index: i
};
});
} else {
return [];
}
}
}
}
}

How to have onclick/hover display associated value in ChartJS

I have a (regularly updating) ChartJS line chart as follows, working off of three types of data - prices, dates, and associated more_info - collected from a Django API:
<script>
var myChart
function refresh_graph() {
{% block jquery %}
var chart_endpoint = "{% url 'chart_data' current_id %}"
var defaultData = []
var labels = []
var more_info = []
$.ajax({
method: "GET",
url: chart_endpoint,
success: function(data){
defaultData = data.prices
labels = data.dates
more_info = data.more_info
if(myChart){
myChart.destroy();
}
var ctx = document.getElementById('myChart').getContext('2d');
myChart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets : [{
label: 'Price',
data: defaultData,
backgroundColor: [
'rgba(54, 162, 235, 0.2)',
],
borderColor: [
'rgba(54, 162, 235, 1)',
],
borderWidth: 2
}]
},
options: {
elements: {
line: {
tension: 0 // disables bezier curves
}
},
legend: {
display: false
},
scales: {
yAxes: [{
ticks: {
suggestedMin: 0,
suggestedMax: 1
}
}]
},
animation: {
duration: 0 // general animation time
},
hover: {
animationDuration: 0 // duration of animations when hovering an item
},
responsiveAnimationDuration: 0 // animation duration after a resize
}
})
}
})
setTimeout(refresh_graph, 5000);
{% endblock %}
}
setTimeout(refresh_graph, 0);
</script>
<div>
<canvas id="myChart" width="1000" height="400"></canvas>
</div>
I'm trying to figure out how to make it so that, when the user clicks on or hovers over (I'd be happy with either) one of the data points in the graph (i.e., a price at a date), they'll see the associated more_info.
I'm aware of this using getElementById, but can't figure out how to extend that to a case like this, where I'm not looking to display a label (here: date) and a data point value (here: price), but rather a third value, i.e., more_info.
I'm also aware of this way of using custom tooltips, but also can't figure out how to extend this from the case where I'm simply using tooltipItem.xLabel (date) and tooltipItem.yLabel (price), as opposed to a third, associated value.
Here's a solution using tooltips (ignoring the Ajax from the OP):
var myChart
function refresh_graph() {
var labels = ["Monday", "Tuesday", "Wednesday","Thursday","Friday","Saturday","Sunday"]
var defaultData = [0.1,0.5,0.3,0.4,0.6,0.8,0.3]
var more_info = ["Monday info", "Tuesday info", "Wednesday info","Thursday info","Friday info","Saturday info","Sunday info"]
if(myChart){
myChart.destroy();
}
var ctx = document.getElementById('myChart').getContext('2d');
myChart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
label: "Price",
data: defaultData,
backgroundColor: 'rgba(54, 162, 235, 0.2)',
borderColor: 'rgba(54, 162, 235, 1)',
borderWidth: 2,
}]
},
options: {
responsive : true,
tooltips : {
callbacks : {
title : function() {
return 'More information:';
},
afterLabel : function(tooltipItem, data) {
return 'Information: ' + more_info[tooltipItem.index];
},
}
},
elements: {
line: {
tension: 0
}
},
legend: {
display: false
},
scales: {
yAxes: [{
ticks: {
suggestedMin: 0,
suggestedMax: 1
}
}]
},
animation: {
duration: 0
},
hover: {
animationDuration: 0
},
responsiveAnimationDuration: 0
}
})
setTimeout(refresh_graph, 50000);
}
setTimeout(refresh_graph, 0);
Full codepen with custom tooltip here: https://codepen.io/kh_one/pen/OJJPBpJ.

Chart.JS - show values on top of points

Good evening,
I'm trying to build a line chart that represents API response time. The problem is that I didn't find any solution in Chart.JS documentation. Is there any native solution or any solution using canvas api?
I want to get the chart looking like this:
Here is the code that I've used to generate the chart
<script>
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: hoursArrFirst,
datasets: [{
label: 'First Brand API',
data: timeArrProftit,
borderWidth: 1,
backgroundColor: [
'rgba(255, 99, 132, 0.05)',
'rgba(255, 159, 64, 0.05)'
],
borderColor: [
'rgba(255,99,132,1)',
'rgba(255, 59, 64, 1)'
]
},{
label: 'Second Brand API',
data: timeArrSecond,
borderWidth: 1,
backgroundColor: [
'rgba(132, 255, 99, 0.05)',
'rgba(64, 255, 159, 0.05)'
],
borderColor: [
'rgba(32,155,99,1)',
'rgba(64,155, 59, 1)'
]
},{
label: 'Third Brand API' ,
data: timeArrThird,
borderWidth: 1,
backgroundColor: [
'rgba(32, 99, 255, 0.05)',
'rgba(64, 59, 255, 0.05)'
],
borderColor: [
'rgba(32, 99, 120, 1)',
'rgba(64, 59, 120, 1)'
]
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero:true
}
}]
}
}
});
</script>
Call a function during and after animation
var options = {
onAnimationProgress: function() { drawDatasetPointsLabels() },
onAnimationComplete: function() { drawDatasetPointsLabels() }
}
function drawDatasetPointsLabels() {
ctx.font = '.9rem sans-serif';
ctx.fillStyle = '#AAA';
ctx.textAlign="center";
$(Trends.datasets).each(function(idx,dataset){
$(dataset.points).each(function(pdx,pointinfo){
// First dataset is shifted off the scale line.
// Don't write to the canvas for the null placeholder.
if ( pointinfo.value !== null ) {
ctx.fillText(pointinfo.value,pointinfo.x,pointinfo.y - 15);
}
});
});
}

Categories

Resources