React & Chartjs: Chart re-rendering on mouseover - javascript

My state is changing based on incoming props, which then triggers a rebuild of the chart. However, what seems to be happening is that when I mouseover the chart it reveals old data, or data that's disappeared then reappears.
Here's a gif showing the problem: https://imgur.com/a/SQbhi9p
And here's my chart code:
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.pricesData !== prevState.pricesData) {
return { pricesData: nextProps.pricesData };
} else {
return null;
}
}
componentDidMount() {
this.buildChart();
}
componentDidUpdate(prevProps) {
if (!_.isEqual(this.props.pricesData, prevProps.pricesData)) {
this.buildChart();
}
}
buildChart fn:
buildChart = () => {
let datasets = [];
if (this.state.pricesData) {
this.state.pricesData.forEach((set) => {
if (set.titel === "Competiors price") {
let obj = {};
obj.label = set.titel;
obj.backgroundColor = set.color;
obj.data = [0, set.price];
obj.tooltip = set.tooltip;
datasets.push(obj);
} else {
let obj = {};
obj.label = set.titel;
obj.backgroundColor = set.color;
obj.data = [set.price, 0];
obj.tooltip = set.tooltip;
datasets.push(obj);
}
});
}
const myChart = new Chart(this.chartRef.current, {
type: "bar",
data: {
labels: ["Change=", "Competitor"],
datasets: datasets,
},
options: {
legend: {
display: false,
},
title: {
display: true,
fontSize: 16,
text: "Your estimated monthly costs",
},
responsive: true,
maintainAspectRatio: false,
scales: {
xAxes: [
{
stacked: true,
},
],
yAxes: [
{
stacked: true,
ticks: {
callback: function (value) {
return "€" + value;
},
},
},
],
},
},
});
this.setState({ chart: myChart });
};

Related

Monitoring datas with HighChart using .net6 and javaScript

I have a BackgroundService class that is load 60 point of data at first load then add a point every minute to chart.I have used SignalR with HighChart in .Net6,the problem is that adding point in javascript class does not work.I want to add a point with it's data's at the end of the chart then shift it .
this is backgroundService Class:
public class TxnRespHistoryBackgroundCaller : BackgroundService
{
public ITxnRespHistoryRepository Repository { get; }
public IHubContext<MonitoringHub> HubContext { get; }
private bool isFirstCall = true;
public TxnRespHistoryBackgroundCaller(ITxnRespHistoryRepository repository, IHubContext<MonitoringHub> hubContext)
{
Repository = repository;
HubContext = hubContext;
}
private static List<string> FailResps
{
get
{
try
{
return new List<string>() { "3", "92", "91", "96", "80", "84" };
}
catch
{
return new List<string>();
}
}
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await Task.Run(async () =>
{
while (!stoppingToken.IsCancellationRequested)
{
if (isFirstCall == true)
{
try
{
var datasets = new List<dynamic>();
DateTime FinishTime = DateTime.Now;
try
{
FinishTime = Repository.GetMax(TxnRespHistory.Columns.EndTime);
}
catch
{
try
{
FinishTime = Repository.GetServerTime();
}
catch
{
FinishTime = DateTime.Now;
}
}
var yesterdayalltxn = await Repository.GetTxnHisByResp(59, TransactionType.Yesterday, FinishTime, FailResps);
var failtxn = await Repository.GetTxnHisByResp(59, TransactionType.fail, FinishTime, FailResps);
var alltxn = await Repository.GetTxnHisByResp(59, TransactionType.All, FinishTime, FailResps);
var suctxn = await Repository.GetTxnHisByResp(59, TransactionType.successful, FinishTime, FailResps);
datasets.Add(new
{
data = suctxn.Value,
label = "Success",
Key = "Success",
color = "#308014"
});
datasets.Add(new
{
data = failtxn.Value,
label = "UnSuccess",
Key = "Fail",
color = "#ff0000"
});
datasets.Add(new
{
data = alltxn.Value,
label = "Total",
Key = "Total",
color = "#7094db"
});
datasets.Add(new
{
data = yesterdayalltxn.Value,
label = "Yesterday",
Key = "Yesterday",
color = "#b3b3b3"
});
var result = new
{
success = true,
labels = alltxn.Key,
datasets = datasets
};
await HubContext.Clients.All.SendAsync("populatetxns", JsonConvert.SerializeObject(result));
isFirstCall = false;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
else
{
try
{
var newtxn = Repository.AddtxnHisByResp(FailResps);
var result = new
{
labels = newtxn.Key,
datasets = newtxn.Value
};
await HubContext.Clients.All.SendAsync("populatetxns", JsonConvert.SerializeObject(result));
Thread.Sleep(10000);
}
catch (Exception)
{
throw;
}
}
}
});
}
}
and this is javaScript Code :
let txnresphisbycha = {};
$(document).ready(function () {
let isFirstCallSuccess = false;
var connection = new signalR
.HubConnectionBuilder()
.withUrl("/monitoringHub").build();
connection.start().then(() => console.log("hubconnected")).catch(() => console.log(error));//establish connection
connection.on("populatetxns", (result) => {
if (isFirstCallSuccess == false)
getChartData(JSON.parse(result));
else
addChartData(JSON.parse(result));
});
function getChartData(result) {
var dataSets = new Array();
for (i = 0; i < result.datasets.length; i++) {
var entity = {};
entity.name = result.datasets[i].label;
entity.data = result.datasets[i].data;
entity.color = result.datasets[i].color;
entity.type = 'area';
dataSets.push(entity);
}
txnresphisbycha = Highcharts.chart('txnhisbyrespcha', {
chart: {
zoomType: 'x',
alignTicks: false,
height: (10 / 22 * 100) + '%',// set ratio
},
title: {
text: 'Total Transaction Chart'
},
credits: {
text: ''
},
legend: {
enabled: true,
rtl: true,
y: 25,
margin: 0,
shadow: true,
verticalAlign: 'top',
itemStyle: {
cursor: "pointer",
}
},
tooltip: {
enabled: true,
rtl: true,
split: false,
shared: true,
xDateFormat: '%H:%M',
useHTML: true,
style: {
fontSize: '15px',
rtl: true,
},
distance: 30,
padding: 5,
headerFormat: '<span>{point.key}</span><br/>'
},
xAxis: {
zoomEnabled: true,
categories: result.labels,
type: 'datetime',
tickInterval: 1,
labels: {
type: 'datetime',
step: 1,
rotation: 55,
style: {
fontSize: '11px'
},
}
},
yAxis: {
title: {
text: "Quantity",
style: {
fontSize: '15px',
},
},
opposite: false,
endOnTick: true,
showLastLabel: true,
labels: {
style: {
fontSize: '15px',
},
align: 'right',
x: -7,
y: 5,
formatter: function () {
return this.value;
}
}
},
plotOptions: {
series: {
pointRange: 1 * 60 * 1000,
fillOpacity: 0.2,
marker: {
symbol: 'circle'
}
}
},
series: dataSets,
navigator: {
enabled: true
}
});
isFirstCallSuccess = true;
}
function addChartData(result) {
if (!isFirstCallSuccess) return;
debugger;
if (txnresphisbycha.series[0].data[txnresphisbycha.series[0].data.length - 1].category != result.labels[0]) {
txnresphisbycha.series[0].addPoint({
x: result.labels[0],
function() {
for (var i in result.datasets) {
y: result.datasets[i]
}
},
dataLables: { enabled: true }
},
true
,true)
txnresphisbycha.update();
}
}
})

Chart.js legend configuration options not working [duplicate]

I am trying to hide the legend of my chart created with Chart.js.
According to the official documentation (https://www.chartjs.org/docs/latest/configuration/legend.html), to hide the legend, the display property of the options.display object must be set to false.
I have tried to do it in the following way:
const options = {
legend: {
display: false,
}
};
But it doesn't work, my legend is still there. I even tried this other way, but unfortunately, without success.
const options = {
legend: {
display: false,
labels: {
display: false
}
}
}
};
This is my full code.
import React, { useEffect, useState } from 'react';
import { Line } from "react-chartjs-2";
import numeral from 'numeral';
const options = {
legend: {
display: false,
},
elements: {
point: {
radius: 1,
},
},
maintainAspectRatio: false,
tooltips: {
mode: "index",
intersect: false,
callbacks: {
label: function (tooltipItem, data) {
return numeral(tooltipItem.value).format("+0,000");
},
},
},
scales: {
xAxes: [
{
type: "time",
time: {
format: "DD/MM/YY",
tooltipFormat: "ll",
},
},
],
yAxes: [
{
gridLines: {
display: false,
},
ticks: {
callback: function(value, index, values) {
return numeral(value).format("0a");
},
},
},
],
},
};
const buildChartData = (data, casesType = "cases") => {
let chartData = [];
let lastDataPoint;
for(let date in data.cases) {
if (lastDataPoint) {
let newDataPoint = {
x: date,
y: data[casesType][date] - lastDataPoint
}
chartData.push(newDataPoint);
}
lastDataPoint = data[casesType][date];
}
return chartData;
};
function LineGraph({ casesType }) {
const [data, setData] = useState({});
useEffect(() => {
const fetchData = async() => {
await fetch("https://disease.sh/v3/covid-19/historical/all?lastdays=120")
.then ((response) => {
return response.json();
})
.then((data) => {
let chartData = buildChartData(data, casesType);
setData(chartData);
});
};
fetchData();
}, [casesType]);
return (
<div>
{data?.length > 0 && (
<Line
data={{
datasets: [
{
backgroundColor: "rgba(204, 16, 52, 0.5)",
borderColor: "#CC1034",
data: data
},
],
}}
options={options}
/>
)}
</div>
);
}
export default LineGraph;
Could someone help me? Thank you in advance!
PD: Maybe is useful to try to find a solution, but I get 'undefined' in the text of my legend and when I try to change the text like this, the text legend still appearing as 'Undefindex'.
const options = {
legend: {
display: true,
text: 'Hello!'
}
};
As described in the documentation you linked the namespace where the legend is configured is: options.plugins.legend, if you put it there it will work:
var options = {
type: 'line',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
borderColor: 'pink'
}
]
},
options: {
plugins: {
legend: {
display: false
}
}
}
}
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.5.0/chart.js"></script>
</body>
On another note, a big part of your options object is wrong, its in V2 syntax while you are using v3, please take a look at the migration guide
Reason why you get undefined as text in your legend is, is because you dont supply any label argument in your dataset.
in the newest versions this code works fine
const options = {
plugins: {
legend: {
display: false,
},
},
};
return <Doughnut data={data} options={options} />;
Import your options value inside the charts component like so:
const options = {
legend: {
display: false
}
};
<Line data={data} options={options} />

Chart.js - how to have statis lables and populate with dynamic data?

I am working on chart.js and I have data coming from JSON via ajax. See the example below:
[{"timestamp":"06:00:00.000000","true_count":2},{"timestamp":"07:00:00.000000","true_count":5},{"timestamp":"08:00:00.000000","true_count":7},{"timestamp":"09:00:00.000000","true_count":8},{"timestamp":"10:00:00.000000","true_count":12},{"timestamp":"11:00:00.000000","true_count":15},{"timestamp":"12:00:00.000000","true_count":20},{"timestamp":"13:00:00.000000","true_count":17},{"timestamp":"14:00:00.000000","true_count":14},{"timestamp":"16:00:00.000000","true_count":11},{"timestamp":"17:00:00.000000","true_count":19},{"timestamp":"18:00:00.000000","true_count":22},{"timestamp":"19:00:00.000000","true_count":16},{"timestamp":"20:00:00.000000","true_count":14},{"timestamp":"22:00:00.000000","true_count":7}]
The JS code i am using for my chart is below:
// create initial empty chart
var ctx_live = document.getElementById("chLine");
var myChart = new Chart(ctx_live, {
type: 'bar',
data: {
labels: [],
datasets: [{
data: [],
borderWidth: 1,
borderColor:'#00c0ef',
label: 'liveCount',
}]
},
options: {
responsive: true,
title: {
display: true,
text: "Count Per Hour",
},
legend: {
display: false
},
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
}
}]
}
}
});
// logic to get new data
var getData = function() {
var _data =[];
var _labels = [];
$.ajax({
url: 'chart_data',
type: "get",
success: function(data) {
full_data = JSON.parse(data);
full_data.forEach(function(key,index) {
_data.push(key.true_count);
_labels.push(key.hour);
});
myChart.data.labels = _labels;
myChart.data.datasets[0].data = _data;
myChart.update();
}
});
};
// get new data every 3 seconds
setInterval(getData, 3000);
Now, this is working fine and shows the true_count over time which is a one-hour basis. Now, the chart is showing only hours with count but what I would like to do is to set the static hours from 12 AM to 11 PM, and for hours for which I don't have data the true_count will be zero, and for those that I have data for, the true count will be assigned to that hour and show on the chart.
Any ideas on how do I do that?
Here is an example:
// create initial empty chart
var ctx_live = document.getElementById("chLine");
var myChart = new Chart(ctx_live, {
type: 'bar',
data: {
labels: [],
datasets: [{
data: [],
borderWidth: 1,
borderColor: '#00c0ef',
label: 'liveCount',
}]
},
options: {
responsive: true,
title: {
display: true,
text: "Count Per Hour",
},
legend: {
display: false
},
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
}
}]
}
}
});
// Some constants to be changed later:
const HOUR_TO_START = 0;
const HOUR_TO_END = 23;
// helper:
const intToAmPm = (i) =>
i==0 ? '12 AM' :
i==12 ? '12 PM' :
i < 12 ? i + ' AM' :
(i-12) + ' PM';
// logic to get new data
var getData = function() {
var _data = [];
var _labels = [];
$ajax({
url: 'chart_data',
type: "get",
success: function(data) {
full_data = JSON.parse(data);
let preparedData = {};
full_data.forEach(function(key, index) {
let hour = parseInt(String(key.timestamp).substring(0, 2));
preparedData[hour] = key.true_count;
});
for (let i = HOUR_TO_START; i <= HOUR_TO_END; i++) {
_data.push(preparedData[i] === undefined ? 0 : preparedData[i]);
_labels.push(intToAmPm(i));
}
myChart.data.labels = _labels;
myChart.data.datasets[0].data = _data;
myChart.update();
}
});
};
// get new data every 3 seconds
//setInterval(getData, 3000);
getData();
// THIS IS FOR TESTING. IMITATE BACKEND
function $ajax(param) {
param.success('[{"timestamp":"06:00:00.000000","true_count":2},{"timestamp":"07:00:00.000000","true_count":5},{"timestamp":"08:00:00.000000","true_count":7},{"timestamp":"09:00:00.000000","true_count":8},{"timestamp":"10:00:00.000000","true_count":12},{"timestamp":"11:00:00.000000","true_count":15},{"timestamp":"12:00:00.000000","true_count":20},{"timestamp":"13:00:00.000000","true_count":17},{"timestamp":"14:00:00.000000","true_count":14},{"timestamp":"16:00:00.000000","true_count":11},{"timestamp":"17:00:00.000000","true_count":19},{"timestamp":"18:00:00.000000","true_count":22},{"timestamp":"19:00:00.000000","true_count":16},{"timestamp":"20:00:00.000000","true_count":14},{"timestamp":"22:00:00.000000","true_count":7}]');
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="chLine"></canvas>

Chart.js show data in chronological order

I have a chart using Chart.js but regardless of what order I put the input data it is outputted out of order, here is a fiddle:
const response = [
{
"mmyy":"12/19",
"promocode":"promo1",
"amount":"2776"
},
{
"mmyy":"01/20",
"promocode":"promo1",
"amount":"1245"
},
{
"mmyy":"01/20",
"promocode":"promo2",
"amount":"179"
}
];
var chartColors = window.chartColors;
var color = Chart.helpers.color;
const colors = [color(chartColors.red).alpha(0.5).rgbString(),
color(chartColors.orange).alpha(0.5).rgbString(),
color(chartColors.yellow).alpha(0.5).rgbString(),
color(chartColors.green).alpha(0.5).rgbString(),
color(chartColors.blue).alpha(0.5).rgbString()];
const labels = Array.from(new Set(response.map(c => c.mmyy))).sort();
const promocodes = Array.from(new Set(response.map(c => c.promocode))).sort();
let i = 0;
const datasets = promocodes.map(pc => ({
label: pc,
data: [],
backgroundColor: colors[i++]
}));
labels.forEach(l => {
for (let pc of promocodes) {
let city = response.find(c => c.mmyy == l && c.promocode == pc);
datasets.find(ds => ds.label == pc).data.push(city ? Number(city.amount) : 0);
}
});
var ctx = document.getElementById('promorChart').getContext('2d');
var chartColors = window.chartColors;
var color = Chart.helpers.color;
var promorChart = new Chart(ctx, {
type: 'bar',
data: {
labels: labels,
datasets: datasets
},
options: {
scales: {
xAxes: [{
stacked: false
}],
yAxes: [{
stacked: false,
ticks: {
// Include a dollar sign in the ticks
callback: function(value, index, values) {
return '$' + value;
}
}
}]
},
tooltips: {
callbacks: {
label: function(tooltipItems, data) {
return "$" + tooltipItems.yLabel.toString();
}
}
},
responsive: true,
elements: {
}
}
});
<canvas id="promorChart"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<script src="https://www.chartjs.org/samples/latest/utils.js"></script>
As you can see it shows 01/20 and then 12/19 when it should be in reverse order.
Can someone tell me how to make it show in chronological order (oldest to newest)?
Thank you very much.
You can use moment.js to parse and help sorting the dates.
const labels = Array.from(new Set(response.map(c => c.mmyy))).sort((d1, d2) => moment(d1, 'MM/YY').diff(moment(d2, 'MM/YY')));
This results in the following runnable code snippet.
const response = [
{
"mmyy":"12/19",
"promocode":"promo1",
"amount":"2776"
},
{
"mmyy":"01/20",
"promocode":"promo1",
"amount":"1245"
},
{
"mmyy":"01/20",
"promocode":"promo2",
"amount":"179"
}
];
var chartColors = window.chartColors;
var color = Chart.helpers.color;
const colors = [color(chartColors.red).alpha(0.5).rgbString(),
color(chartColors.orange).alpha(0.5).rgbString(),
color(chartColors.yellow).alpha(0.5).rgbString(),
color(chartColors.green).alpha(0.5).rgbString(),
color(chartColors.blue).alpha(0.5).rgbString()];
const labels = Array.from(new Set(response.map(c => c.mmyy))).sort((d1, d2) => moment(d1, 'MM/YY').diff(moment(d2, 'MM/YY')));
const promocodes = Array.from(new Set(response.map(c => c.promocode))).sort();
let i = 0;
const datasets = promocodes.map(pc => ({
label: pc,
data: [],
backgroundColor: colors[i++]
}));
labels.forEach(l => {
for (let pc of promocodes) {
let city = response.find(c => c.mmyy == l && c.promocode == pc);
datasets.find(ds => ds.label == pc).data.push(city ? Number(city.amount) : 0);
}
});
var ctx = document.getElementById('promorChart').getContext('2d');
var chartColors = window.chartColors;
var color = Chart.helpers.color;
var promorChart = new Chart(ctx, {
type: 'bar',
data: {
labels: labels,
datasets: datasets
},
options: {
scales: {
xAxes: [{
stacked: false
}],
yAxes: [{
stacked: false,
ticks: {
// Include a dollar sign in the ticks
callback: function(value, index, values) {
return '$' + value;
}
}
}]
},
tooltips: {
callbacks: {
label: function(tooltipItems, data) {
return "$" + tooltipItems.yLabel.toString();
}
}
},
responsive: true,
elements: {
}
}
});
<canvas id="promorChart"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
<script src="https://www.chartjs.org/samples/latest/utils.js"></script>
You can use reverse for this
...
scales: {
xAxes: [{
stacked: false,
reverse: true // ADD THIS LINE
}],
yAxes: [{
stacked: false,
ticks: {
// Include a dollar sign in the ticks
callback: function(value, index, values) {
return '$' + value;
}
}
}]
},
...

FlotChart do not work zooming mode

I have following example: http://jsfiddle.net/ondra15/7mb8K/1/.
I want to have together two example (multiple axes and zooming). Example zooming do not work correct - if I indicate some data in chart for zooming - do not work. Nothing happens.
Original Zooming (correct) solution works here http://www.flotcharts.org/flot/examples/zooming/index.html
Can any some idea for my code? Thanks
code
Hi I managed to get it working using code from an example I found here. I will update your jsfiddle too:
<script type="text/javascript">
var datasets = { ... };
data = null;
function plotByChoice(doAll) {
data = [];
if (doAll != null) {
$.each(datasets, function (key, val) {
data.push(val);
});
}
else {
$('#legend .legendCB').each(
function () {
if (this.checked) {
data.push(datasets[this.id]);
}
else {
data.push({ label: this.id, data: [] })
}
}
);
}
$.plot($("#placeholder"), data, {
yaxes: [{ min: 0 }, { position: "right" }],
xaxis: { tickDecimals: 0 },
legend: {
container: legend,
labelFormatter: function (label, series) {
var cb = '<input class="legendCB" type="checkbox" ';
if (series.data.length > 0) {
cb += 'checked="true" ';
}
cb += 'id="' + label + '" /> ';
cb += label;
return cb;
}
}, selection: { mode: "x" }
});
$('#legend').find("input").click(function () { setTimeout(plotByChoice, 100); });
}
plotByChoice(true);
// Create the overview plot
var overview = $.plot("#overview", data, {
legend: {
show: false
},
series: {
lines: {
show: true,
lineWidth: 1
},
shadowSize: 0
},
xaxis: {
ticks: 4
},
yaxes: [{ min: 0 }, { position: "right" }],
grid: {
color: "#999"
},
selection: {
mode: "x"
}
});
$("#placeholder").bind("plotselected", function (event, ranges) {
var options = {
series: {
lines: { show: true },
points: { show: true }
},
legend: { noColumns: 2 },
xaxis: { tickDecimals: 0 },
yaxis: { min: 0 },
selection: { mode: "x" }
};
var placeholder = $("#placeholder");
placeholder.bind("plotselected", function (event, ranges) {
$("#selection").text(ranges.xaxis.from.toFixed(1) + " to " + ranges.xaxis.to.toFixed(1));
plot = $.plot(placeholder, data,
$.extend(true, {}, options, {
xaxis: { min: ranges.xaxis.from, max: ranges.xaxis.to }
}));
});
// don't fire event on the overview to prevent eternal loop
overview.setSelection(ranges, true);
});
$("#overview").bind("plotselected", function (event, ranges) {
plot.setSelection(ranges);
});
</script>

Categories