Edit: Here is a Fiddle - slightly different (simplified) code to examples below but same problem.
I have a simple Highcharts bar chart showing two series of data for 2011 (truncated):
var dataset = {};
dataset.attchange2011 = [
{y: -8.5},
{y: -8.3}
];
dataset.revchange2011 = [
{y: -14.9},
{y: -10.7}
];
This is displaying properly in the graph on load:
...
series: [{
name: 'Change in Revenue',
data: dataset['revchange2012']
},{
name: 'Change in Attendance',
data: dataset['attchange2012']
}]
...
I have a second set of data for 2012:
dataset.attchange2012 = [
{y: 1.2 },
{y: 14.1}
];
dataset.revchange2012 = [
{y: 11.5},
{y: 37.5}
];
And a simple function to switch between the years by getting the data-year value from a link that's clicked, loop through that year's data and update the series points values, followed by a redraw:
...
year = $(this).data('year').toString();
$.each(chart.series[0].data, function (i, point) {
point.update(dataset['revchange'+year][i], false);
});
$.each(chart.series[1].data, function (i, point) {
point.update(dataset['attchange'+year][i], false);
});
...
The first time the 2011 link is clicked, the data updates correctly. Trying to switch back to 2012 doesn't work.
When looking at each data set at various stages by console.log(dataset), it appears that it is correctly set on page load:
dataset
Object
attchange2011: Array[8]
0: Object
y: -8.5
...
attchange2012: Array[8]
0: Object
y: 1.2
But changes when the link is clicked - 2011 values are copied to the 2012 data set:
dataset
Object
attchange2011: Array[8]
0: Object
y: -8.5
...
attchange2012: Array[8]
0: Object
y: -8.5
I can't figure out where or why it would be doing that. Any ideas? I am not completely against rewriting everything from scratch if needed.
It's caused by Highcharts. Variables are overwritten when updating points. Instead, use copy of that objects: http://jsfiddle.net/RF7aW/7/
series: [$.extend(true, {}, data2013[0]), $.extend(true, {}, data2013[1])],
If not against rewriting, I suggest a much cleaner approach which would be this:
Define an actual function, function makeChart(data) and initialize it like you do now, giving it the 2012 data. On click of 2012 or 2011 or whatever other button, simply call makeChart(otherData). This extracts the whole updating issue which, by the way, you don't need to iterate through every single point, you can just update the data series entirely with series.update (which I believe redraws anyways).
EDIT:
If not redrawing, try using simple buttons and using series.setData() instead:
JSfiddle
Related
Let's take a look at the simple chart data:
it has (x,y) pairs:
x
y
0
-3
1
2
2
7
3
8
4
15
5
0
The idea is to create a basic line chart, using VueJS in my case, but the idea can be generalized to JavaScript.
I have a series array of objects, where each object has x and y coordinates:
series = [
{
x: 0,
y: -3
},
{
x: 1,
y: 2
},
...
]
This series is part of options object:
const options = {
chart: {
type: 'line'
},
series: series
}
const chart = new ApexCharts(document.querySelector("#chart"), options);
chart.render();
And the chart is rendered.
Now, let's say I want to append the data to that chart - add 2 new (x,y) pairs
const newData = [
{
x: 6,
y: 20
},
{
x: 7,
y: -10
}
]
chart.appendData([{ data: chartData }])
I would also like that newly appendedData has, for example, different color, fill, or something else - so newly added data displays differently than old data.
Feel free to point me to documentation if I missed anything, but I searched through apex chart methods, and the only thing that looks remotely close to this would be inside
updateOptions() method, the redrawPath flag (updateOptions docs:
When the chart is re-rendered, should it draw from the existing paths
or completely redraw the chart paths from the beginning. By default,
the chart is re-rendered from the existing paths
In order to style the new data differently, you'll want to put these data points into a different series using the appendSeries method:
const newData = [
{
x: 6,
y: 20
},
{
x: 7,
y: -10
}
]
chart.appendSeries({
name: "series-2", // optional
data: newData
})
Most of the styling in ApexCharts is done based on series (more specifically seriesIndex). So by placing the new data in a separate series you'll be able to style this second series using an array of, for example, colors.
You could either specify the color you would like to use as you append the new series of data using the updateOptions method you mention, or you can specify it in advance.
chartOptions: {
colors: ["#546E7A", "#E91E63"],
}
When working with "Numeric paired values in XY properties", the xaxis type also has to be explicitly set to numeric:
chartOptions: {
xaxis: {
type: 'numeric',
},
}
The tricky bit comes when you want to add more data a second time (or third, more time). There are two approaches I can think of here:
Shuffle the existing data across to the original series (append series-2 to series-1) - and overwrite series-2 with your new data. You don't need to edit the colors in chartOptions.
You could shuffle the colors along. If you want all "old" data to have the same color, simply prepend the colors array with your base color every time you add a new series. Or if you want each series to have a different color, just append a color every time you add a new series.
I was not able to find an answer to the following what I think is best described by example:
I change one single value in radar-chart-data from 6 to 10 while the other ones stay the same. If I now trigger an update with non-zero animation-time it will repaint the chart while starting from zero for each value. I would prefer the animation to just animate the changing, i.e. a movement from 6 to 10 for the property in question. Is that possible?
Below is an example showing the chart updating without redrawing. Run the snippet and press the 'Update' button to increase data point b by 1 each time.
let myChart = new Chart(document.getElementById('chart'), {
type: 'radar',
data: {
labels: ['a', 'b', 'c', 'd', 'e'],
datasets: [{
label: 'series1',
data: [0, 2, 7, 10, 3]
}]
},
options: {
maintainAspectRatio: false
}
});
document.getElementById('update').addEventListener('click', function() {
myChart.data.datasets[0].data[1] += 1;
myChart.update();
});
<button id="update">Update!</button>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.min.js"></script>
<canvas id="chart"></canvas>
Yes. It is possible. It depends on how you update the chart option. See chartjs documentations
To update the options, mutating the options property in place or passing in a new options object are supported.
If the options are mutated in place, other option properties would be preserved, including those calculated by Chart.js.
If created as a new object, it would be like creating a new chart with the options - old options would be discarded.
I'm trying to solve an odd problem. I'm getting a json array back from an ajax call and I'm attempting to plot it in highcarts. I've mapped other graphs from the same array and all is well, up until the point I hit decimal numbers (maybe co-incidence). In this case the dates show fine but the y-axis (prices) is empty.
Now, I can 'alert(s5)' and the data displays on the alert box as it should.
I also ran a consol log and see "["5.15", "4.94", "4.43", "4.49", "4.42", "4.41"]" (maybe the " in the numbers is causing the issue!?)
If I put the values manually into the highcharts data it works perfectly but I just can't assign the array to a value and get it to display.
code looks like:
function draw_flow(garray)
{
var obj = JSON.stringify(garray);
obj = JSON.parse(obj);
s5 = obj["closeprice"][0];
ticks = obj["date"][0];
alert(s5); //THIS DISPLAYS DATA FINE! "5.15,4.94,4.43,4.42,4.41"
$('#chart3').highcharts({
chart: {
marginBottom: 80
},
xAxis: {
categories: ticks
},
yAxis: {
labels: {
align: 'left',
x: 0,
y: -2
}
},
series: [{
data: s5 //This does not work
//data: [5.15,4.94,4.43,4.42,4.41] //this works
}]
});
}
You are correct. The reason the Y axis is not displaying is because of the Strings in your data. (which should be read as numbers)
You have to convert your data from Strings to an array of numbers which can be achieved with the following
s5 = s5.map(Number);
This is an example jsFiddle which shows it in action http://jsfiddle.net/e803sjsp/
A bit cheeky but have you checked that s5 is actually an array and not a string of comma separated values. You might want to try a console.log to be sure..
I'm being able to use JQuery Flot, and it's a very nice tool. However, I could not find a GOOD solution for my problem.
I want to duplicate Y axis, so I can display 1 on the left and 1 on the right, so the users, when comparing data from the rightmost side of the chart, won't have to scroll through the leftmost side of the chart. I'm assuming they will be accessing it through a smartphone.
JQuery Flot allows multiple axis, but for each axis, I would need a different set of data, as in this example:
http://people.iola.dk/olau/flot/examples/multiple-axes.html
But I don't want to duplicate the data. Can't I just 'tell' Flot to duplicate the yaxis using the same set of data?
You can use the hooks functionality to force flot to show the second yaxis even though it has no data series assigned to it:
// hook function to mark axis as "used"
// and assign min/max from left axis
pOff = function(plot, offset){
plot.getYAxes()[1].used = true;
plot.getYAxes()[1].datamin = plot.getYAxes()[0].datamin;
plot.getYAxes()[1].datamax = plot.getYAxes()[0].datamax;
}
$.plot("#placeholder2", [ { data: d2 } ], {
hooks: { processOffset: [pOff] },
yaxes: [ {},
{position: 'right'} // add second axis
]
});
Depending on how your axis is configured though, this might be messy. You'll have to steal parameters from the left axis to get it to work (as I've done above with datamin/datamax).
If it was my code, I'd go with your duplicate data approach. You aren't really duplicating anything, just assigned the same array to two series. I'd then configure the 2nd series to simply not draw.
var d2 = [[0, 3], [4, 8], [8, 5], [9, 13]];
// use the same data but toggle off the lines...
$.plot("#placeholder", [ { data: d2 }, {data: d2, yaxis: 2, lines: {show: false}} ], {
yaxes: [ {},
{position: 'right'} ]
});
Here's a fiddle demonstrating the two approaches.
I have an array of data points that I am passing to a Highcharts chart that looks like
mydata = [{
x: 1,
y: 3,
nameList: ["name1", "name2"]
}, {
x: 2,
y: 4,
nameList: ["name3", "name4"]
}]
I build the chart like this:
$("#chart").highcharts("StockChart", {
series: [{
data: mydata
}, {
data: yourdata
}]
});
Now, I would like to be able to access the nameList array from the shared tooltip, which I'm trying to do as follows:
tooltip: {
formatter: function() {
var s = "";
$.each(this.points, function(i, point) {
s += point.point.nameList;
});
return s;
},
shared: true
}
but when examining the point objects in Firebug using console.log(point), I can't seem to find the nameList entry anywhere in them. How could I access this auxiliary information in a shared series tooltip? All help is appreciated.
Eureka!
By default, Highcharts will accept several different types of input for the data of a series, including
An array of numerical values. In this case, the numberical values will be interpreted
and y values, and x values will be automatically calculated, either starting at 0 and
incrementing by 1, or from pointStart and pointInterval given in the plotOptions.
An array of arrays with two values. In this case, the first value is the x value and the
second is the y value. If the first value is a string, it is applied as the name of the
point, and the x value is incremented following the above rules.
An array of objects with named values. In this case the objects are point configuration
objects as seen below.
However, the treatment of type 3 is different from types 1 and 2: if the array is greater than the turboThreshold setting, then arrays of type 3 won't be rendered. Hence, to fix my problem, I just needed to raise the turboThreshold setting like so:
...
plotOptions: {
line: {
turboThreshold: longestArray.length + 1
}
},
...
and the chart renders the longestArray data properly. Hurray! The only drawback is that there is a considerable time spent rendering the data for much longer arrays due to "expensive data checking and indexing in long series." If any of you know how I might be able to bypass this checking or otherwise be able to speed up the processing of this data, I'd be extremely thankful if you'd let me know how.
I can see it here:
tooltip: {
formatter: function() {
var s = "";
console.log(this.points[0].point.nameList); // ["name1", "name2"]
$.each(this.points, function(i, point) {
s += point.point.nameList;
});
return s;
},
shared: true
}