Highcharts: series does not update if previous array only had null values - javascript

I have an Highchart which updates the series. In some cases (like the fiddle shows), the update does not work correctly.
When the series had an array which only had null-values, the next given array does not take effect.
https://jsfiddle.net/ChrisCross82/ubz3o4pr/
<body>
<button onclick="firstData()">1st Data</button>
<button onclick="secondData()">2nd Data</button>
<button onclick="firstData()">Again 1st Data (without Series1)</button>
<div id="chart1" style="height: 300px"></div>
</body>
var chart1;
chart1 = Highcharts.chart('chart1', {
series: [{
data: [],
},{
data: [],
}]
});
function firstData() {
var series1 = [65.4, 72.7, 70, 60.6, 42.9];
var series2 = [26, 33, 10, 33, 7];
updateChart(series1, series2);
}
function secondData() {
var series1 = [null, null, null, null, null];
var series2 = [0, 0, 0, 0, 0];
updateChart(series1, series2);
}
function updateChart(series1, series2){
chart1.update({
series: [{
data: series1
},{
data: series2,
}]
});
}
For now, I check the array, and if every value in the array is null, I set the array to []. After this, the update works. But I think this is not the best solution.

This problem looks like a bug, so I reported it on Highcharts github: https://github.com/highcharts/highcharts/issues/9290
It is worth noting that only update with the same number of data causes the issue: https://jsfiddle.net/BlackLabel/8nbdy9wq/
To workaround, you can update the chart with null values as y property:
function secondData() {
var series1 = [{
y: null
}, {
y: null
}, {
y: null
}, {
y: null
}, {
y: null
}];
var series2 = [0, 0, 0, 0, 0];
updateChart(series1, series2);
}
Live demo: https://jsfiddle.net/BlackLabel/cnw4vh65/

Related

Set a limit / max number for legends

Lets say I have a Donut chart with 5 items in data like this
const data = {
labels: ['E-commerce', 'Enterprise', 'Green', 'Grey', 'Purple'],
datasets: [
{
label: '# of Votes',
data: [12, 19, 3, 5, 3],
backgroundColor: ['#C07CC3', '#9C3848', '#9DDBAD', '#ADA8B6', '#606EDA'],
borderWidth: 1,
},
],
}
I don't want it to show all the legends as I don't have space or whatever reason
How can I hide the Green and purple in this example?
I mean only from legends not from chart
I see two easy ways, how you could approach this problem:
(I personally I would use the second option below, it is more configurable, but it needs just abit of coding)
You can simply delete the labels you want to delete from the labels- array, and they won't show up in the legend.
But keep in mind you would have to change the order of the data and backgroundColor arrays, to match this change.
Here a short demo:
const data = {
labels: ['E-commerce', 'Enterprise', 'Grey'], // <-- just remove the unwanted labels
datasets: [{
data: [12, 19, 5, 3, 3], // <-- reorder
backgroundColor: ['#C07CC3', '#9C3848', '#ADA8B6', '#9DDBAD', '#606EDA'], // <-- reorder
borderWidth: 1,
}],
};
const config = {
type: 'doughnut',
data: data,
options: {
maintainAspectRatio: false,
plugins: {
legend: {
position: 'right',
labels: {
usePointStyle: true,
},
}
},
}
};
new Chart(
document.getElementById('chart'),
config
);
<script src="//cdn.jsdelivr.net/npm/chart.js"></script>
<div class="chart" style="height:184px; width:350px;">
<canvas id="chart" ></canvas>
</div>
Better and cleaner (but some coding is needed), you can filter out label-items, you don't want to display, with the function array filter. (details can be found in the documentation)
UPDATED Alternative Version Demo:
here only the Top 3 labels (limiting the amount of labels with the variable maxLabelsToShow) will be shown (sort order is descending, but changing this is would be easy)
function getLabelsOnlyTopX(num, data, labels){
let selectedLabels = []
//we don't want to alter the order
let helperData = [...data];
//sort in descending order
helperData.sort( (a,b) => b-a);
//get top X Values
helperData = helperData.slice(0, num);
//get index for the data
let indexes = data.map( (value, index) => ({value,index}) ).filter(item => helperData.some(n1 => n1 == item.value))
//slecet only labels with the correct index
selectedLabels = labels.filter((value, index) => indexes.some( n => n.index == index))
// just be sure that a maximum of num labels are sent
return selectedLabels.slice(0, num);
}
let maxLabelsToShow = 3;
let serverData = [12, 19, 3, 5, 3]
let labels = ['E-commerce', 'Enterprise', 'Green', 'Grey', 'Purple'];
// Calling the newly created function
let showOnly = getLabelsOnlyTopX(maxLabelsToShow, serverData, labels);
const data = {
labels: labels,
datasets: [{
data: serverData,
backgroundColor: ['#C07CC3', '#9C3848',
'#9DDBAD', '#ADA8B6', '#606EDA'],
borderWidth: 1,
}],
};
const config = {
type: 'doughnut',
data: data,
options: {
maintainAspectRatio: false,
plugins: {
legend: {
position: 'right',
labels: {
usePointStyle: true,
/* FILTER function */
filter: function(item, chart) {
return showOnly.indexOf( item.text) > -1;
}
},
}
},
}
};
new Chart(
document.getElementById('chart'),
config
);
<script src="//cdn.jsdelivr.net/npm/chart.js"></script>
<div class="chart" style="height:184px; width:350px;">
<canvas id="chart" ></canvas>
</div>

Problems Populating Chart.js OHLC Financial Chart from JSON Array (Ajax call from Django model)

Has anyone had success dynamically creating a Chart.js OHLC financial chart from a JSON array? In this case, I'm using an Ajax call to import the data from a Django model:
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js" integrity="sha512-ElRFoEQdI5Ht6kZvyzXhYG9NqjtkmlkfYk0wr6wHxU9JEHakS7UJZNeml5ALk+8IKlU6jDgMabC3vkumRokgJA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
<script src="..\static\stocks\js\chart.js" type="text/javascript"></script>
<canvas id="myChart" width="300" height="175"></canvas>
<script>
let priceArray = []
const endpoint2 = '/api/stockArray/'
$.ajax({
method: 'GET',
url: endpoint2,
success: function(stockArray){
priceArray = stockArray
setChart(priceArray)
}
})
function setChart() {
let testClose = priceArray.map(e => e.data_close)
let testLabels = priceArray.map(e => new Date(e.data_date).setHours(0, 0, 0, 0))
let testOpen = priceArray.map(e => e.data_open)
let testHigh = priceArray.map(e => e.low)
let testLow = priceArray.map(e => e.high)
const data = {
labels: [],
datasets: [{
type: 'ohlc',
label: 'Stocks',
backgroundColor: '#ffffff',
borderColor: '#000000',
data: [
{
x: testLabels,
o: testOpen,
h: testHigh,
l: testLow,
c: testClose,
}
]
}]
}
const config = {
data,
options: {
scales: {
x: {
type: 'timeseries',
time: {
unit: 'day'
}
},
y: {
beginAtZero: true,
max: 3
}
}
}
};
new Chart(
document.getElementById('myChart'),
config
);
}
</script>
I've followed this tutorial (https://www.youtube.com/watch?v=Mzt7IOKnhDw) and successfully created a static OHLC chart. I've also successfully populated a Chart.js line and bar chart dynamically from the same array. In the case of the OHLC chart, I keep getting a RangeError with an Invalid Time Value from the chartjs-adapter-date-fns.bundle. I've tried three methods of populating the x scale from the date values (stored as testLabels) in the array:
let testLabels = priceArray.map(e => new Date(e.data_date).setHours(0, 0, 0, 0))
let testLabels2 = priceArray.map(e => new Date(e.data_date))
let testLabels3 = priceArray.map(e => e.data_date)
Same error every time. If I log these, the values show as
testLabels = [1450328400000, 1450846800000...]
testLabels2 = [Thu
Dec 17 2015 19:00:00 GMT-0500 (Eastern Standard Time), Wed Dec 23
2015 19:00:00 GMT-0500 (Eastern Standard Time)...]
testLabels3 =
['2015-12-18', '2015-12-24'...]
I'll add one more caveat that's throwing me off. If you populate the chart with static values, this code works:
data: [
{
x: new Date ('2022-08-10').setHours(0, 0, 0, 0),
o: 1.00,
h: 1.35,
l: 0.90,
c: 1.20,
},
{
x: new Date ('2022-08-11').setHours(0, 0, 0, 0),
o: 1.00,
h: 1.35,
l: 0.90,
c: 1.20,
}
]
However, if you only pass one object, the chart appears blank:
data: [
{
x: new Date ('2022-08-10').setHours(0, 0, 0, 0),
o: 1.00,
h: 1.35,
l: 0.90,
c: 1.20,
}]
Makes me think it's not entirely a date/time issue with the axis. The tutorial only came out a month ago, so maybe this plugin still needs some development. I'm happy to explore other js libraries, but this is the exact chart style I'm looking for. Happy to provide any additional information. Thanks in advance.
Marking this as answered. I pivoted to canvasjs because I prefer the chart options. But, as I said, you have to push each data point to the end of the array before populating the chart, as shown here (spline instead of OHLC, but same principle):
window.onload = function () {
var chart = new CanvasJS.Chart("chartContainer", {
title: {
text: "Adding & Updating dataPoints"
},
data: [
{
type: "spline",
dataPoints: [
{ y: 10 },
{ y: 4 },
{ y: 18 },
{ y: 8 }
]
}
]
});
chart.render();
$("#addDataPoint").click(function () {
var length = chart.options.data[0].dataPoints.length;
chart.options.title.text = "New DataPoint Added at the end";
chart.options.data[0].dataPoints.push({ y: 25 - Math.random() * 10});
chart.render();

How to remove x axis scale labels Chart.Js [duplicate]

This question already has answers here:
Chart.js v2 hide dataset labels
(9 answers)
Closed 1 year ago.
Chart
I created a chart using chart.js and it works as expected. The problem is that I don't need the x axis labels (Speed). I looked at questions similar to this but none of them worked. How do you hide the x axis labels without removing or changing anything else in chart.js v3?
You can do 2 things, either supply an labels array with empty strings or use the tick callback to provide empty labels
scales: {
x: {
ticks: {
// Include a dollar sign in the ticks
callback: function(value, index, values) {
return '';
}
}
}
}
Example:
var options = {
type: 'line',
data: {
// option 1, provide empty strings for labels array
labels: ["", "", "", "", "", ""],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
borderWidth: 1
},
{
label: '# of Points',
data: [7, 11, 5, 8, 3, 7],
borderWidth: 1
}
]
},
options: {
scales: {
x: {
ticks: {
//option 2, use callback to change labels to empty string
callback: () => ('')
}
}
}
}
}
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.1.1/chart.js" integrity="sha512-aWx9jeVTj8X49UzUnUHGIlo6rNne1xNsCny/lL0QwUTQK2eilrHXpSk9xbRm4FJ4eLi2XBmnFlRkWPoChSx8bA==" crossorigin="anonymous"></script>
</body>
Simply disable the x axis labels by one flag like this:
options: {
scales: {
x: {
ticks: {
display: false
}
}
}
}

Growing chart value - chart.js

I want to do so the chart value increases itself by 1 each second. How can I achieve that? I tried setting a counter variable and then increasing it every second but it didn't work dynamically for some reason.
var myChart = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
label: 'Crash line',
data: [0, 1], //1 here needs to be increased so the graphs grows up
backgroundColor: [
'rgba(108, 170, 91, 0.2)'
],
borderColor: [
'rgba(255,99,132,1)'
],
borderWidth: 1
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
}
});
Check out this chart.js documention and chart.js update documention.
You need to use the chart.update method as so:
var options = //chart.js option object
var ctx = document.getElementById('chartJSContainer').getContext('2d');
var chart = new Chart(ctx, options);
var somefunc = function(chart){
var chartDatasetLength = chart.data.datasets[0].data.length;
for(var i = 0; i < chartDatasetLength; i++){
chart.data.datasets[0].data[i] += 1;
}
chart.update();
}
setInterval(somefunc, 1000, chart);
A working jsfiddle demo.

jQuery Flot Chart add label per series

I am using http://www.flotcharts.org/ library to draw 2 connected series.
I can create Graph successfuly, you can see my JSFIDDLE below.
To do:
The problem is I want to add one label to the top-center of each series.
And I want to add label in such a way that if graph is resized it stays at top-center of series.
How can I do that ?
IMAGE:
JSFIDDLE:
http://jsfiddle.net/bababalcksheep/ktZ5X/2/
HTML/CSS:
html, body {height: 100%; }
JS:
var options = {
series: {
lines: { show: true,lineWidth: 2,fill: true},
points: { show: true,radius: 3,lineWidth: 1},
shadowSize: 2
},
legend: { show: false}
};
var DATA = [{
"label": "Group-1",
"data": [
[0.25, 0.25],
[0.5, 0.5],
[0.875, 0.875],
[1, 1]
]
}, {
"label": "Group-2",
"data": [
[1, 0.5],
[1.25, 0.25],
[1.5, 0.5],
[1.88, 0.875],
[2, 1]
]
}];
var plot = $.plot("#placeholder", DATA, options);
I agree with #iLikePrograms adding it in a hook event is the best approach. Here's an updated fiddle.
In your options add:
hooks: { drawSeries : [addLabel] }
Where the addLabel function is:
addLabel = function(plot, ctx, series){
var xaxis = series.xaxis;
var yaxis = series.yaxis;
// space midway on series
var x = (series.data[series.data.length-1][0] + series.data[0][0])/2;
// just below top of plot
var y = yaxis.max - (yaxis.tickSize / 2);
ctx.font = "16px 'Segoe UI'";
ctx.fillStyle = "black";
var text = series.label;
ctx.fillText(text, xaxis.p2c(x), yaxis.p2c(y)); // add the label
}
You can use the comments plugin for that. See this updated fiddle for a start.
New options:
comment: {
show: true
},
comments: [{
x: 0.625,
y: 1,
contents: "Group-1"
}, {
x: 1.5,
y: 1,
contents: "Group-2"
}]

Categories

Resources