I'm struggling to solve a problem I have with getting a chart to be redrawn after navigating to another view and then return back to the same chart view.
I'm using Backbone.js and Underscore as a MVC and templating solution for my application.
When I navigate to the charting page initially it works great, but if I move away and then come back I get a `TypeError: chart.series[0] is undefined in the console log.
I have highlighted line where the error occurs in the code below, close to the end.
My thought is that I may need to destroy() the chart at some point, but I'm unsure where I would do this and if it would solve my problem.
I recently changed the script to now use HighStock 'Highstock JS v1.3.6 (2013-10-04)' from earlier HighCharts ver 2.3.5, where it appeared that I did not have this issue.
The app is live online if anyone really needs or wants to see it in action, let me know.
The following code section is the function thatis called by the Backbone router.
If there is a need for other sections of code, please let me know.
I'll be grateful for any advice.
EDIT:
I have managed to get this in JSFiddle for anyone to view the problem.
Clickty clack your fury tailless one here http://jsfiddle.net/rockwallaby/pqKWj
When you first run it up, you will get a trend being displayed.
Then hit the 'Go to a different page' link, you will get a fairly blank page.
On that page there is a link to bring you back to the trend page.
Hitting that link brings you back but the trend does not get rendered correctly due to the above mentioned error.
//=================================================================================
// trendsBattery
// A simple trend view showing Battery Volts and Solar Charge
//
window.trendsBattery = Backbone.View.extend({
trendModel: new TrendsModel(),
template: _.template(trendsTemplate),
chart: null,
chartoptions:{
chart: {
renderTo: 'chart-container',
},
rangeSelector: {
enabled: false,
},
title:{
text:'Battery Volts & Solar Amps'
},
xAxis: {
type:'datetime',
dateTimeLabelFormats: {month:'%e. %b',year:'%b'}
},
yAxis: [{
title :{text: 'Battery Volts'},
min: 22,
max: 32,
minorGridLineColor: '#E0E0E0',
},
{
title :{text: 'Solar Charge Amps'},
min: 0,
max: 16,
opposite: true,
},
],
series:[
{yAxis: 0, data: [], type: 'line', step: true, name: 'Battery Vdc'},
{yAxis: 1, data: [], type: 'line', step: true, name: 'Solar Amps'},
],
},
render:function() {
that = this;
$(this.el).html(this.template());
this.chartoptions.chart.width = (windowWidth);
this.chartoptions.chart.height = (windowHeight - 150);
setTimeout(function() {
chart = new Highcharts.StockChart(that.chartoptions);
chart.events ={load: that.requestData(this.chart) };
},20);
return this;
},
requestData: function(chart){
var querystring = '//myHostServer.com/myFolder/myPHP.php';
jQuery.get(querystring, null, function(csv, state, xhr) {
if (typeof csv !== 'string') {
csv = xhr.responseText;
};
csv = csv.split(/\n/g);
var vB_array = [];
var iS_array = [];
jQuery.each(csv, function (i, line) {
if (line.length > 1) {
line_array = line.split(',');
var date = parseInt(line_array[0]) * 1000;
var vBpoint = {};
var iSpoint = {};
vBpoint.x = date;
iSpoint.x = date;
vBpoint.y = parseFloat(line_array[1]);
iSpoint.y = parseFloat(line_array[4]);
vB_array.unshift(vBpoint);
iS_array.unshift(iSpoint);
};
});
chart.series[0].setData(vB_array, false); // <<<<< Problem Area
chart.series[1].setData(iS_array, false);
chart.redraw();
});
chart.xAxis[0].setExtremes(); // expand out the scrollbar when moving through time:
},
});
I just updated jsfiddle with the suggestion I provided in comment, it's working.
window.trendView = Backbone.View.extend({
trendModel: new TrendsModel(),
template: _.template(trendTemplate),
chart: null,
getChartOptions: function () {
return {
chart: {
animation: true,
renderTo: 'chart-container',
backgroundColor: '#fff'
},
rangeSelector: {
enabled: false
},
title: {
text: 'Battery Volts & Solar Amps'
},
xAxis: {
type: 'datetime',
dateTimeLabelFormats: {
month: '%e. %b',
year: '%b'
} // don't display the dummy year:
},
yAxis: [{
title: {
text: 'Battery Volts'
},
min: 22,
max: 32,
minorGridLineColor: '#E0E0E0'
}, {
title: {
text: 'Solar Charge Amps'
},
min: 0,
max: 16,
opposite: true
}],
series: [{
yAxis: 0,
data: [],
type: 'line',
step: true,
name: 'Battery Vdc'
}, {
yAxis: 1,
data: [],
type: 'line',
step: true,
name: 'Solar Amps'
}]
}
},
render: function () {
that = this;
$(this.el).html(this.template());
var chartOptions = that.getChartOptions();
chartOptions.chart.width = (windowWidth - 50);
chartOptions.chart.height = (windowHeight - 50);
setTimeout(function () {
chart = new Highcharts.StockChart(chartOptions);
chart.events = {
load: that.requestData(this.chart)
};
}, 20);
return this;
},
requestData: function (chart) {
var querystring = '//paulalting.com/hydrosolar/clientGET.php?id=trendVolts&start=6400&size=200';
console.log(querystring);
jQuery.get(querystring, null, function (csv, state, xhr) {
if (typeof csv !== 'string') {
csv = xhr.responseText;
}
csv = csv.split(/\n/g);
var vB_array = [];
var iS_array = [];
jQuery.each(csv, function (i, line) {
if (line.length > 1) {
line_array = line.split(',');
var date = parseInt(line_array[0], 10) * 1000;
var vBpoint = {};
var iSpoint = {};
vBpoint.x = date;
iSpoint.x = date;
vBpoint.y = parseFloat(line_array[1]);
iSpoint.y = parseFloat(line_array[4]);
vB_array.unshift(vBpoint);
iS_array.unshift(iSpoint);
}
});
chart.series[0].setData(vB_array, false); // <<<<< Problem Area
chart.series[1].setData(iS_array, false);
chart.redraw();
});
chart.xAxis[0].setExtremes(); // expand out the scrollbar when moving through time:
}
});
here is the link
http://jsfiddle.net/pqKWj/14/
If you are interested let me know I see some more similar problems in the code, which I think fine for now, but will create issues, when you have 2 charts in the same page etc. we can talk
Related
I am using .net core 2.0 web socket to generate some data. I want to send the data to this chart and draw the chart.
In the code below I want to show the live data without default values. I tried a lot but I wasn't successful. If I remove default value from 'series', chart will disappear. IS that possible to draw this chart on the fly?
Highcharts.setOptions({
global: {
useUTC: false
}});
Highcharts.stockChart('container', {
chart: {
events: {
load: function () {
// set up the updating of the chart each second
var series = this.series[0];
setInterval(function () {
var x = (new Date()).getTime(), // current time
y = Math.round(Math.random() * 100);
series.addPoint([x, y], true, true);
}, 1000);
}
}
},
rangeSelector: {
buttons: [{
count: 1,
type: 'minute',
text: '1M'
}, {
count: 5,
type: 'minute',
text: '5M'
}, {
type: 'all',
text: 'All'
}],
inputEnabled: false,
selected: 0
},
title: {
text: 'Live random data'
},
exporting: {
enabled: false
},
series: [{
name: 'Random data',
data: (function () {
// generate an array of random data
var data = [],
time = (new Date()).getTime(),
i;
for (i = -999; i <= 0; i += 1) {
data.push([
time + i * 1000,
Math.round(Math.random() * 100)
]);
}
return data;
}())
}]});
Finally I could find the solution. I needed to add xAxis property to my chart. By adding the code below, problem solved. Now it starts from current time.
xAxis: {
type: 'datetime',
tickPixelInterval: null,
maxZoom: 10 * 1000,
min: new Date().getTime()
}
I am trying to dynamically load in a number of series, depending on projects chosen by the user. I am using Laravel 5.2, PHP 5.6 and HighCharts.
I have managed to load in one JSON file, which is generated when the user selects projects. But I would like it if the JavaScript could parse the different series from the JSON file and dynamically load this into the series.
This is the code which I am using:
$(function () {
// Set up the chart
var processed_json = new Array();
$.getJSON('/uploads/test.json', function(data) {
for (i = 0; i < data.length; i++) {
processed_json.push([data[i].key, data[i].value]);
}
var chart = new Highcharts.Chart({
chart: {
renderTo: 'container',
type: 'column',
options3d: {
enabled: true,
alpha: 0,
beta: 0,
depth: 0,
viewDistance: 25
}
},
title: {
text: 'Grades'
},
subtitle: {
text: 'Dataset'
},
plotOptions: {
column: {
depth: 0
}
},
series: [{
name: 'Grades',
data: processed_json
}],
credits: {
enabled: false
}
});
function showValues() {
$('#alpha-value').html(chart.options.chart.options3d.alpha);
$('#beta-value').html(chart.options.chart.options3d.beta);
$('#depth-value').html(chart.options.chart.options3d.depth);
}
// Activate the sliders
$('#sliders input').on('input change', function () {
chart.options.chart.options3d[this.id] = this.value;
showValues();
chart.redraw(false);
});
showValues();
});
});
My JSON is formatted like:
[{"key":"Math","value":6},{"key":"Biology","value":"8"},{"key":"English","value":"7"},{"key":"Gym","value":"4"}]
So I would like to have more JSONs like so, in one file to be parsed in the Javascript and be loaded in into the series.
Thank you!
EDIT
thanks for your reply. I have edited my code:
$(function () {
var processed_json = new Array();
var options = {
chart: {
renderTo: 'container',
type: 'column',
options3d: {
enabled: true,
alpha: 0,
beta: 0,
depth: 0,
viewDistance: 25
}
},
title: {
text: 'Grades'
},
subtitle: {
text: 'Dataset'
},
plotOptions: {
column: {
depth: 0
}
},
series: [{
}],
credits: {
enabled: false
}
};
$.getJSON('/uploads/test.json', function(data) {
for (i = 0; i < data.length; i++) {
processed_json.push([data[i].key, data[i].value]);
}
});
options.series[0].remove();
options.series[0].setData = {
name: 'Grades',
data: processed_json
}
var chart = new Highcharts.Chart(options);
chart.redraw(true);
function showValues() {
$('#alpha-value').html(chart.options.chart.options3d.alpha);
$('#beta-value').html(chart.options.chart.options3d.beta);
$('#depth-value').html(chart.options.chart.options3d.depth);
}
// Activate the sliders
$('#sliders input').on('input change', function () {
chart.options.chart.options3d[this.id] = this.value;
showValues();
chart.redraw(false);
});
showValues();
});
But nothing is displayed anymore and the following error is given
TypeError: options.series[0].remove is not a function
I have also tried
var chart = new Highcharts.Chart(options);
chart.series[0].remove();
chart.series[0].setData = {
name: 'Grades',
data: processed_json
}
chart.redraw(true);
But this gives:
TypeError: Cannot set property 'setData' of undefined
I think highcharts has a method chart.addSeries for adding a new series. If you want to replace the current series with a new series, you can try removing first the current series using chart.series[0].remove( ) then add the new series with chart.addSeries. The parameter for the chart.addSeries can be an object like your
{
name: 'Grades',
data: processed_json
}
Then, define method with load data.. example:
function(chart) {
$.each(chart.series, function(i, v){
chart.series[i].remove(true);
});
chart.addSeries({your_data}, true);
}
check http://api.highcharts.com/highcharts/Chart.addSeries/Chart.addSeries
In my webapp, i use 10 of 15 types of graphs highchart and all dynamically load and work fine :) Highcharts is awesome.
So without jquery I want to update highcharts with new data live. I have a chart that displays data from a database, and I am doing a http get request to get the data every few seconds. I am able to grab the data correctly, but when I push the new data onto the series variable for the chart, the graph doesn't update in real time. It only updates when I refresh. How can I fix this? I am using highcharts in angularjs.
you should call series.addPoint() instead of just updating the data array
please see here http://jsfiddle.net/9m3fg/1
js:
var myapp = angular.module('myapp', ["highcharts-ng"]);
myapp.controller('myctrl', function ($scope) {
$scope.addPoints = function () {
var seriesArray = $scope.chartConfig.series
var newValue = Math.floor((Math.random() * 10) + 1);
$scope.chartConfig.xAxis.currentMax++;
//if you've got one series push new value to that series
seriesArray[0].data.push(newValue);
};
$scope.chartConfig = {
options: {
chart: {
type: 'line',
zoomType: 'x'
}
},
series: [{
data: [10, 15, 12, 8, 7, 1, 1, 19, 15, 10]
}],
title: {
text: 'Hello'
},
xAxis: {
currentMin: 0,
currentMax: 10,
minRange: 1
},
loading: false
}
});
From your code it looks like you want to add new series rather then new data if yes please see here: http://jsfiddle.net/bYx4a/
var app = angular.module('app', ["highcharts-ng"]);
app.controller("myCtrl", ['$scope', '$http', function ($scope, $http) {
var count = 0;
$scope.chartOptions = {
chart: {
type: 'line'
},
title: {
text: 'Fruit Consumption'
},
xAxis: {
categories: ['Apples', 'Bananas', 'Oranges']
},
yAxis: {
title: {
text: 'Fruit eaten'
}
},
series: [{
name: 'Jane',
data: [1, 0, 4]
}]
};
$scope.addSeries = function () {
var newData = {
name: 'John',
data: [1, 4, 3]
};
$scope.chartOptions.series.push({
name: newData.name,
data: newData.data
})
};
}]);
Here is my solution for using Highcharts addPoint function in the highcharts-ng directive:
$scope.chart_realtimeForceConfig = {
options: {
chart: {
type: 'line',
},
plotOptions: {
series: {
animation: false
},
},
},
series: [
{
name: 'Fx',
data: []
},
],
func: function(chart) {
$timeout(function() {
chart.reflow();
$scope.highchart = chart;
}, 300);
socket.on('ati_sensordata', function(data) {
if (data) {
var splited = data.split('|');
if (splited.length >= 6) {
var val = parseFloat(splited[5]);
var shift = chart.series[0].data.length > 100;
chart.series[0].addPoint(val, true, shift, false);
}
}
});
},
loading: false
}
I'm using Highcharts.visualize to draw the graph from a table containing the data.
You can test my working code here: http://jsfiddle.net/S2XM8/1/
I have two questions:
I want to have a separate styling for my "Additional value". How do I go about it?
Can I add data for the X-axis via the javascript? For example if I need to fill in the gap between 2014-05-27 and 2014-05-25 in the table.
Highcharts.visualize = function (table, options, tableClass) {
// the categories
options.xAxis.categories = [];
$('tbody th', table).each( function () {
options.xAxis.categories.push(this.innerHTML);
});
// the data series
options.series = [];
$('tr', table).each( function (i) {
var tr = this;
$('.graph', tr).each( function (j) {
if (i === 0) { // get the name and init the series
options.series[j] = {
name: this.innerHTML,
data: []
};
} else { // add values
options.series[j].data.push(parseFloat(this.innerHTML));
console.log(this.innerHTML);
}
});
});
options.title = { text: 'Some graph' };
$('#' + tableClass + '-graph').highcharts(options);
};
var tableNumber = document.getElementById('rank-table'),
options = {
chart: {
zoomType: 'x'
},
xAxis: {
tickInterval: 30,
reversed: true,
labels: {
rotation: 45
},
type: 'datetime',
dateTimeLabelFormats: { // don't display the dummy year
month: '%e. %b',
year: '%b'
}
},
yAxis: {
title: {
text: 'Rank'
},
min: 1,
reversed: true
},
legend: {
layout: 'vertical',
align: 'middle',
verticalAlign: 'bottom',
borderWidth: 0
}
};
Highcharts.visualize(tableNumber, options, 'number');
Both things are possible, but require to modify visualize method, see: http://jsfiddle.net/S2XM8/4/
You can set series options in a chart and then merge with data:
series: [{
// nothing special
}, {
type: 'column' // set series type for example
}]
And merging:
options.series[j] = options.series[j] || {};
options.series[j].name = this.innerHTML,
options.series[j].data = [];
Check parsed value before passing as point value:
var value = parseFloat(this.innerHTML);
if(isNaN(value)) { //null value - produces NaN when parsing
options.series[j].data.push(10);
} else {
options.series[j].data.push(value); // push value to the series
}
I'm plotting Csv column data in highcharts. Instead of the:
$.get('5.csv', function(data)
I want input a local desktop Csv file using:
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
My current Javascript code is below :
var options = {
chart: {
renderTo: 'container',
defaultSeriesType: 'line'
},
title: {
text: 'Test'
},
xAxis: {
categories: []
},
yAxis: {
title: {
text: 'Units',
}
},
series: []
};
// $.get('5.csv', function(data) {
var file = event.target.file;
var reader = new FileReader();
var txt=reader.readAsText(file);
var lines = txt.split('\n');
var c = [], d = [];
$.each(lines, function(lineNo, line) {
if(lineNo > 0 ){
var items = line.split(',');
var strTemp = items[0];
c = [parseFloat(items[0]), parseFloat(items[1])];
d.push(c);
console.log(c);
}
});
options.xAxis.categories = c;
options.series = [{
data: d
}];
chart = new Highcharts.Chart(options);
});
How would I go about doing this ? I want to upload a Csv file from a local desktop machine. How do I link the File Reader upload of the file to highcharts to plot, instead of using the $.get(5.csv', function(data) { ? Or am I better using jquery-csv (https://github.com/evanplaice/jquery-csv). I know there are browser security issues. My file is a 3 column Csv with a one line header, column 1 is the x-axis, 2 is the y-axis, 3 will be the error bar, which I haven't yet implemented:
Q,I,E
0.009,2.40E-01,5.67E-02
0.011,2.13E-01,3.83E-02
0.013,2.82E-01,2.28E-02
etc ....
This works now upload by File API
function processFiles(files) {
var chart;
options = {
chart: {
zoomType: 'x',
renderTo: 'container',
type: 'line',
zoomType: 'x'
},
title: {
text: ''
},
subtitle: {
text: ''
},
xAxis: {
type: 'linear',
minorTickInterval: 0.1,
title: {
text: 'Q'}
},
yAxis: {
type: 'linear',
minorTickInterval: 0.1,
title: {
text: 'I(ntensity)'
},
},
tooltip: {
shared: true
},
legend: {
enabled: true
},
plotOptions: {
area: {
fillColor: {
linearGradient: [0, 0, 0, 300],
stops: [
[0, Highcharts.getOptions().colors[0]],
[0, 'rgba(2,0,0,0)']
]
},
lineWidth: 1,
marker: {
enabled: false,
states: {
hover: {
enabled: true,
radius: 5
}
}
},
shadow: false,
states: {
hover: {
lineWidth: 1
}
}
}
},
series: [{
name: 'Series'}]
};
var file = files[0]
var reader = new FileReader();
reader.onload = function (e) {
str = e.target.result;
var lines = str.split("\n");
var c = [], d = [], er = [];
$.each(lines, function(lineNo, line) {
if(lineNo > 0 ){
var items = line.split(',');
var strTemp = items[0];
er = parseFloat(items[2])
a = parseFloat(items[0])
b = parseFloat(items[1])
min = (b - (er/2))
max = b + ((er/2))
c = [a , b];
var q = [], e = [];
q = [min, max]
e.push(q);
d.push(c);
console.log(c);
console.log(q);
}
});
options.xAxis.categories = c.name;
lineWidth: 1
options.series = [{
data: d,
type: 'scatter'
}, {
name: 'standard deviation',
type: 'errorbar',
color: 'black',
data : e }
];
$("#Linear").click(function(){
$('#container').highcharts().yAxis[0].update({ type: 'linear'});
});
$("#Log").click(function(){
$('#container').highcharts().yAxis[0].update({ type: 'logarithmic'});
});
$("#Guinier").click(function(){
$('#container').highcharts().yAxis[0].update({ data: Math.log(d)});
options.xAxis.categories = c.name;
lineWidth: 1
options.series = [{
data: d
}]
});
chart = new Highcharts.Chart(options);
}
reader.readAsText(file)
var output = document.getElementById("fileOutput")
};
Due to security reasons you can't load a file directly on the client-side
To do this you need to use the HTML5 File API which will give the user a file dialog to select the file.
If you plan to use jquery-csv here's an example that demonstrates how to do that.
File Handling Demo
I'm biased but I say use jquery-csv to parse the data, trying to write a CSV parser comes with a lot of nasty edge cases.
Source: I'm the author of jquery-csv
As an alternative, if jquery-csv doesn't meet your needs, PapaParse is very good too.