Highcharts shift taking off too many points at the start - javascript

My chart has multiple series and when I try to add a new point and enable shift to true in the addpoint function on the series it seems to take too many points off. My requestData function runs an ajax query which hits my api and brings back data. The initial load loads in data just fine, however after the first points being added to the series it seems to take off too many points off some of the series.
Here is what it looks initial load
https://gyazo.com/51a2afc465f8fbb625e384e75d52b865
and here is what it looks like after one and two requests:
https://gyazo.com/0b523e4675c681b7495a38660b4de72c
https://gyazo.com/5cf7e9498ab3b34f3eaac1cf50a0bdc5
Here is my code for requesting data:
function requestData(chart1, start, devID, attrib) {
$.ajax({
url: 'api/MetricsAPI/',
data: {
deviceID: devID,
attribute: attrib,
startDate: start
},
success: function (dataset) {
var chartSeries = chart1.series;
for (var index = 0; index < dataset.length; index++) {
for (var i = 0; i < chartSeries.length; i++) {
if (chartSeries[i].name == dataset[index].attribute) {
var shift = chartSeries[i].data.length > 12;
chartSeries[i].addPoint([parseInt(dataset[index].epochTime), dataset[index].value], true, shift);
console.log('date: ' + dataset[index].epochTime + ', ' + 'value: ' + dataset[index].value);
}
}
}
chart1.redraw();
//call it again after one minute
setTimeout(requestData, 30000, chart1, dataset[dataset.length - 1].epochTime, devID, attrib);
},
failure: function (xhr, error) {
console.log(xhr);
console.log(error);
},
cache: false
});
}

Two possible issues with current code:
set redraw param to false, here: chartSeries[i].addPoint([parseInt(dataset[index].epochTime), dataset[index].value], true, shift);
set series.id and avoid comparing chartSeries[i].name == dataset[index].attribute. Instead to get correct series use chart.get(id).

Related

JavaScript offset/pagination issue

I am calling to my local API and trying to do it in a pagination style. I have n pictures that I want divided over n / 4 rows (4 pictures per row).
So therefor, I am calling to my API, images/count,offset. But somehow I keep on getting the same results in console.log, namely the first four images.
$(document).ready(function() {
var offset = 0;
var timesToRun = $('.container').data('rows');
var images = [];
for(var i = 1; i <= timesToRun; i++) {
$.ajax('http://192.168.10.11/images/4,' + offset, {
error: function(err) {
console.log(err);
},
method: 'GET',
success: function(data) {
console.log('http://192.168.10.11/images/4,' + offset);
offset = offset + 4;
var currentSet = [];
currentSet.push(data);
console.log(currentSet);
}
});
}
});
In Laravel I am pulling the number of images like so:
public function selectWithOffset($count, $offset)
{
$selectionOfImages = \DB::table('images')->skip($offset)->take($count)->get();
return response()->json($selectionOfImages);
}
When I click the links I do receive the expected response.
What might go wrong here?
The problem is within your JavaScript. $.ajax is asynchronous by default.
The for loop will complete before any success callback of $.ajax is called, and this is the place where you increase the offset.
You have to options to fix this:
1. Make $.ajax synchronous
Add async: false to the $.ajax options.
$.ajax('http://192.168.10.11/images/4,' + offset, {
error: function(err) {
console.log(err);
},
async: false,
method: 'GET',
success: function(data) {
// ...
}
});
2. Increment offset outside of the success callback
for(var i = 1; i <= timesToRun; i++) {
$.ajax('http://192.168.10.11/images/4,' + offset, {
// ...
});
// Increment offset
offset += 4;
}

Javascript with AJAX inside another function

Javascript with AJAX inside another function
http://jsfiddle.net/gh/get/jquery/1.9.1/highslide-software/highcharts.com/tree/master/samples/highcharts/demo/dynamic-update/
From this example, instead of placing the first 19 points with random values, I want to pass a value from my server through AJAX.
And I am talking about the code here.
series: [{
name: 'Random data',
data: (function () {
var data = [],
time = (new Date()).getTime(),
i;
for (i = -19; i <= 0; i += 1) {
data.push({
x: time + i * 1000,
y: Math.random()
});
}
return data;
}())
}]
And since the key of series is also data I have no idea how I am gonna get data from AJAX GET call.
The AJAX call that I want to use is:
$.ajax({
type: "GET",
url: "/getData",
success: function(data) {
var y1 = data.count;
series.addPoint([x, y1], true, true);
}
});
But I tried to use this but it does not seem to work, like the following:
series: [{
name: 'Random data',
data: (function () {
var data1 = [],
time = (new Date()).getTime(),
i;
$.ajax({
type: "GET",
url: "/getData",
success: function(data) {
var y1 = data.count;
for (i = -19; i <= 0; i += 1) {
data1.push({
x: time + i * 1000,
y: data.count
});
}
}
});
return data1;
}())
}]
Please let me know how to GET for the Highchart data
First off see this reference for why you can't return data from your outer function like you're trying to:
How do I return the response from an asynchronous call?
Then, understanding that you will have to use the data from the success handler, that means that you will have to move the ajax call outside of the data declaration and do it afterwards, but you will have to recognize that the data will NOT be available until sometime later when the ajax call finishes. You cannot use it immediately. If you need to know when the data is available, then put the data into the data structure and call some other function from the success handler.
Like they said the AJAX call is async = not blocking, it means that the browser is making the ajax call in your function and instantly goes on at the next line, in your case return data1 but the data1 var is not updated since the ajax call is still being executed.
Documentation:http://api.jquery.com/deferred.done/
There are also some things I don't understand in your code, did you try to lint hit with JSHint or JSLint?, here is my version with some corrections:
// define var outside of function so they are not scoped
var time = (new Date()).getTime(),
data1,series;
$.ajax({
type: "GET",
url: "/getData",
success: function(data) {
// since you are using i only here just define it in the cycle
for (var i = -19; i <= 0; i += 1) {
data1.push({
x: time + i * 1000,
y: data.count
});
}
}
}).done(function() {
series = {
name: 'Random data',
data: data1
};
});

Clean way to make Subsequent AJAX Calls to API based on Data

So I have a conceptual question regarding the cleanest way to make subsequent AJAX calls to an API based on the returned data.
A quick example:
A function, which encompasses the call would look like this:
function makeCall(headers, min, max) {
$.ajax({
headers: headers,
url: "https://coolapi.com/data?begIndex" + min + "&endIndex=" + max + "&begTimestamp=1404198000000&endTimestamp=1409554800000",
type: "GET",
dataType: 'JSON'
});
}
makeCall(headers, 0, 20);
The beg / end index (min/max), determine the amount of data I'll get back in the array. The API will only return a maximum of 20 items in the array, but it will also return me a COUNT of how many items total exist in that array. An example of the data returned is below:
{
count = 133;
result = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19];
}
So my next call would be:
makeCall(headers, 20, 40);
and so on so forth, until I got all 133 items from the array.
The question is...what is the cleanest way to continue to make subsequent calls until I've gotten and stored all 133 items from the array? Given that the count could be any number, it's hard to imagine how I can do this. I was thinking of nesting more ajax calls in a "success" function, but it's not scalable if I get back a number like 300.
Does anyone have any advice on how to proceed?
Thanks in advance!
EDIT:
So based on the advice in the comment, I've attemped to make the call recursive--but it doesn't seem to function as intended:
var theData = [];
var minCounter=0;
var maxCounter= minCounter + 20;
function makeCall(headers, min, max) {
$.ajax({
headers: headers,
url: "https://coolapi.com/data?begIndex" + min + "&endIndex=" + max + "&begTimestamp=1404198000000&endTimestamp=1409554800000",
type: "GET",
dataType: 'JSON',
success: function (data) {
theData.push(data.result);
newMin = minCounter + 20;
if (data.count >= theData.length ) {
makeCall(headers, newMin, maxCounter);
}
}
});
}
makeCall(headers, minCounter, maxCounter);
How do properly increment the variable as well as set the flag?
SECOND EDIT:
The method below works using the second comment's suggestion, but there are some issues here as well...
function doAjax(headers, min, dObject) {
var max = min + 20;
$.ajax({
headers: headers,
url: "https://coolapi.com/data?begIndex" + min + "&endIndex=" + max + "&begTimestamp=1404198000000&endTimestamp=1409554800000",
type: "GET",
dataType: 'JSON',
success: function (data) {
results.push(data);
window.count = data.count;
dObject.resolve();
}
});
}
// array that will contain all deferred objects
var deferreds = [];
// array that will contain all results
var results = [];
// make the ajax calls
for (var i = 20; i < 133 ; i+= 20) {
var dObject = new $.Deferred();
deferreds.push(dObject);
doAjax(headers, i, dObject);
}
// check if all ajax calls have finished
$.when.apply($, deferreds).done(function() {
console.log(results);
});
var dObject = new $.Deferred();
doAjax(headers,0, dObject);
First, the data doesn't push to the array in order. There doesn't seem anyway to fix this. Also strangely enough, in the for loop--I have to set the number for it to actually work. Trying to store it in a variable doesn't seem to work as well...Suggestions here?
Here's a working implementation based around the code you started with. Code is commented to help you understand what is happening:
// Change these constants to suit your purposes.
var API_URL = 'https://coolapi.com/data';
var HEADERS = {};
var API_RESULTS_PER_REQUEST = 20;
var MAX_API_CALLS = 20;
// Count API calls to trigger MAX_API_CALLS safety lock.
var apiCalls = 0;
// Function we'll call to get all our data (see bottom).
function collectApiData(begTimestamp, endTimestamp) {
var dataReady = jQuery.Deferred();
var params = {
'begTimestamp': begTimestamp,
'endTimestamp': endTimestamp
};
var datasetsCollected = requestDatasets(params);
jQuery.when(datasetsCollected).then(function(data) {
dataReady.resolve(data);
});
return dataReady;
}
// Makes individual AJAX call to API
function callApi(params, headers) {
var $request = jQuery.ajax({
url: API_URL,
headers: headers,
data: params,
type: 'GET',
dataType: 'JSON'
});
return $request;
}
// Recursive function that makes API calls until data is collected, there is an
// error, or MAX_API_CALLS limit is hit.
function requestDatasets(params, resultsReady, resultsFetched) {
resultsReady = ( resultsReady !== undefined ) ? resultsReady : jQuery.Deferred();
resultsFetched = ( resultsFetched !== undefined ) ? resultsFetched : [];
// Trigger safety to avoid API abuse
if ( apiCalls >= MAX_API_CALLS ) {
console.error('Exceeded max API calls:', MAX_API_CALLS);
resultsReady.resolve(resultsFetched);
}
// Set index data
params.begIndex = resultsFetched.length;
params.endIndex = resultsFetched.length + API_RESULTS_PER_REQUEST;
// Request dataset from API
var apiRequest = callApi(params, HEADERS);
apiCalls += 1;
// Callback once API request has completed and data is ready
jQuery.when(apiRequest).done(function(data) {
var apiResultCount = data.count;
resultsFetched = resultsFetched.concat(data.result);
console.debug('Fetched', resultsFetched.length, 'of', apiResultCount, 'API results');
if ( apiResultCount > resultsFetched.length ) {
console.debug('Making another API call');
requestDatasets(params, resultsReady, resultsFetched);
}
else {
console.debug('Results all fetched!');
resultsReady.resolve(resultsFetched);
}
});
jQuery.when(apiRequest).fail(function(data) {
console.error('API error: returning current results.');
resultsReady.resolve(resultsFetched);
});
return resultsReady;
}
// Run script
var dataReady = collectApiData('1404198000000', '1409554800000');
jQuery.when(dataReady).then(function(data) {
console.log(data);
});
Here's a working fiddle that mocks the API using httpbin.org:
http://jsfiddle.net/klenwell/mfhLxun2/

Highcharts MVC Unable to get value of the property 'series': object is null or undefined

I am trying to run this example of Highcharts in IE9
http://www.highcharts.com/studies/live-server.htm
but instead of calling the php script i am doing the following:
function requestData() {
var MyAppUrlSettings = {
MyUsefulUrl: '/PaymentConfirmation/UpdateChart?numSecondsToGoBack=3600'
}
$.ajax({
url: MyAppUrlSettings.MyUsefulUrl,
type: "POST",
async: false,
dataType: "json",
success: function (PaymentConfirmationData) {
$.each(PaymentConfirmationData, function (i, PaymentConfirmation) {
var d = new Date();
d.setTime(parseInt(PaymentConfirmation.TimeMilliSeconds));
var milli = d.getTime();
var series = chart.series[0],
shift = series.data.length > 20; // shift if the series is longer than 20
// add the point
chart.series[0].addPoint([milli, PaymentConfirmation.Count], true, shift);
// call it again after one second
setTimeout(requestData, 1000);
});
},
error: function (xhr, status, error) {
alert("An AJAX error occured: " + status + "\nError: " + error);
},
cache: false
});
}
Now like in the example the chart object is declared globally (and i think that is somehow not working!!!)
Note: the "$(document).ready(function() {" function is as it is in the example.
Please help and thanks much in advance.
Without seeing the actual chart code here is my best guess. You did not define your series object correctly. This line is the issue I think:
var series = chart.series[0],
I believe that should be a ; instead of a ,.
Also, if you still get this error then you created a chart with no series but then try to push a point to a series[0]. Make sure you create an empty series (or create a series after chart creation). Like in the example page given:
series: [{
name: 'Random data',
data: []
}]

How do I pass a variable inside of an ajax call which is inside of a for loop (javascript)?

Working with an api and I need to one of the first responses alongside with the second response in order to serve up a new page. The problem I'm facing is that my variable $x is always set to whatever the last # is in the loop, ie 103 in this specific case. Here is my code:
$.ajax({
dataType: 'text',
type: 'post',
url: 'getAllMessages.php',
success: function(responseData) {
var newString = responseData;
var newerString = newString.substring(0, newString.length - 1);
$newObject = jQuery.parseJSON(newerString);
//console.log($newObject);
for($x = 0; $x < $newObject.messages.length; $x++){
$.ajax({
data: {clientFolderId: $newObject.messages[$x].clientFolderId, messageId: $newObject.messages[$x].messageId},
dataType: 'text',
type: 'post',
url: 'testapi.php',
success: function(responseData2){
//alert($x);
var newString2 = responseData2;
var newerString2 = newString2.substring(0, newString2.length - 1);
$newObject2 = jQuery.parseJSON(newerString2);
if($newObject2.statistics.delivered > 1000){
console.log($newObject.messages[$x]);
console.log($newObject2);
}
},
error: function(responseData2){
alert('failure in testapi.php');
}
});
}
},
error: function(responseData) {
alert('failure in getAllMessages.php');
}
});
My intuition says nesting the Ajax call inside another functional scope (correction thanks to Matt) will resolve the unexpected behavior. I got burned by this already Object creation in loop broken; unrolled works
Also here, example #5: http://www.javascriptkit.com/javatutors/closures2.shtml
Following the pattern given by Engineer,
for($x = 0; $x < $newObject.messages.length; $x++){
(function($x) {
$.ajax({
data: {clientFolderId: $newObject.messages[$x].clientFolderId, messageId: $newObject.messages[$x].messageId},
dataType: 'text',
type: 'post',
url: 'testapi.php',
success: function(responseData2){
alert($x);
var newString2 = responseData2;
var newerString2 = newString2.substring(0, newString2.length - 1);
$newObject2 = jQuery.parseJSON(newerString2);
if($newObject2.statistics.delivered > 1000){
console.log($newObject.messages[1]);
console.log($newObject2);
}
},
error: function(responseData2){
alert('failure in testapi.php');
}
});
})($x);
}
What you're experiencing is closure. When the loop spins round, the value for $x is updated. However, when the ajax function comes to grab it - it's using a reference. So as you find, you end up with the last value.
Try and think more functional? What are you trying to do? Let's say you're trying to postMessage - wrap that in a function and pass in the message.
Your code will become easier to read, and you won't get your variables mangled.
I was about to throw out some code, but noticed something I wanted to clarify - you're using the index in both loops to get a single message from a messages array, yet the POST to testapi.php seems to be working on a single message? What kind of response is expected from that?
 Update: Created jsFiddle to Demo Problem
Here you go here's some code to help you out.
function correctOutputPlox(id) {
setTimeout(function() {
$("#output").append("<li>" + id + "</li>");
}, 500);
}
function runNicely() {
// same loop...
for (var x = 0; x < 10; x++) {
// but rather than use 'x' (which is going to change, we pass it's value into a function which doesn't have access to the original 'x' since it's in a different lexical scope.
correctOutputPlox(x);
}
}
function showProblem() {
for (var x = 0; x < 10; x++) {
setTimeout(function() {
$("#output").append("<li>" + x + "</li>");
}, 500);
}
}
showProblem();
runNicely();

Categories