Make a jquery ajax POST inside a loop - javascript

I have a bunch of data inside a loop I'd like to POST to the server via jQuery.
My code is similar to the following:
var patients = [] // contains an array of patient objects I want to POST to server
var post = function(theUrl, theData, callback){
$.ajax({
type: "POST",
url: theUrl,
data: theData,
success: callback,
contentType: "application/json"
});
}
var createdPatient = function(patient){
//patient was created
}
$('#saveAll').click(function(event) {
for (var i = 0;i < patients.length;i++) {
var json = JSON.stringify(patients[i]);
post("/openmrs/ws/rest/v1/patient", json, createdPatient);
}
});
When I run the code only the last patient has been saved to the server. How may I correct this erroneous outcome?

Taking advantage of the promise returned by jQuery.ajax(), you can write something more like this (see comments for detail) :
var patients = [...] // contains an array of patient objects to be POSTed to the server
$('#saveAll').click(function(event) {
// first, map the `patients` array to an array of jqXHR promises as returned by $.ajax().
var promises = patients.map(function(patient) {
return $.ajax({
type: 'POST',
url: '/openmrs/ws/rest/v1/patient',
data: patient, // jQuery.jax will handle js plain objects here. You may need to stringify here if patient is not a plain object.
contentType: "application/json"
}).then(function(data, textStatus, jqXHR) {
return textStatus; // report successes in the form of the "textStatus" message (or anything you like).
}, function(jqXHR, textStatus, errorThrown) {
return $.when(textStatus || errorThrown); // report error on the success path, otherwise `$.when()` will bail out at the first error.
});
});
// Now aggregate the `promises` array with `$.when()`
$.when.apply(null, promises).then(function(results) {
console.log(results);
}, function(error) {
// due to error handling above, you should never get here.
console.log(error);
});
});
For more detail, see jQuery.ajax() and jQuery.when()

Related

Best approach to making sequential POST requests in Javascript

So I have an array that contains data that needs to be sent as part of the payload for POST requests that need to be made. Now, these need to be made sequentially, since I need to display the particular payload , then the result(the result being the response that is returned after making the POST request with the payload), then the next payload, then the next result and so on, on my page. I was just wondering what would be the best approach to accomplish this.So this is how I'm approaching it at the moment:
for (var i = 0; i < quotesList.length; i++) {
var response = $.ajax({
type: "POST",
url: url,
data: JSON.stringify(quotesList[i]),
success: add_to_page,
},
timeout: 300000,
error: function(){
console.log("Some Error!")
},
contentType: "application/json"
})
Obviously, the issue here that arises is there's no guarantee in terms of the getting that sequence right and also I wasn't quite sure how exactly to keep a track of the payload since trying to add it as a part of the success function only gets me the last element in the last(presumably the last request thats fired)
You can try something like below.
let quotesListIndex = 0;
function sendAjaxCall() {
if (quotesListIndex < quotesList.length) {
var response = $.ajax({
type: "POST",
url: url,
data: JSON.stringify(quotesList[quotesListIndex++]),
success: function () {
//... Add code here
sendAjaxCall(); // Call the function again for next request
},
timeout: 300000,
error: function () {
console.log("Some Error!")
},
contentType: "application/json"
})
} else {
return;
}
}
Add one global variable to track the index. and call function recursively. Once the index reached the array size then return from function.

Make JSON loaded via function available to other functions

I'm trying to load JSON with a JS function, and then make the JSON objects that are loaded available to other functions in the same namespace. I've tried using return to serve up the array of objects retrieved, but that doesn't work. In the attached example, I've assigned the array of objects to a property in the namespaced object, but when I try to get that array of objects outside the main loadData function, all I get is null.
Here's my JS:
var myObj = {
jsonEndPoint: '/test/test.json',
dataObjects: null
}
myObj.loadData = function () {
$.ajax({
url: 'test.json',
type: 'POST',
dataType: 'json',
success: function (data) {
myObj.dataObjects = data.apiResults[0].league.season.draft.rounds[0].picks;
//console.log(myObj.dataObjects);
},
error: function (xhr, textStatus, errorThrown) {
console.log('Data Load Error: ' + textStatus);
}
});
}()
myObj.displayData = function() {
console.log(myObj.dataObjects)
}()
The full example can be seen here: http://zbl.me/test/index.html
The JSON file I'm loading is here: http://zbl.me/test/test.json
That is because JavaScript is asynchronous in nature — when you attempt to access the myObj.dataObjects in the myObj.displayData function, that object does not yet exist because the AJAX call has not been completed yet.
What you could do is that ensure that all functions that require newly added data from the AJAX call be run only when a .done() promise has been delivered from your AJAX call, by using $.when(). The logic is quite straightforward:
myObj.loadData() is now exclusively used to make the AJAX call. With regards to how we handle the done and fail events (which are previously .success() and .error() callbacks), we delegate that logic to the next function.
myObj.displayData() is now use to evaluate the promise returned by your AJAX call made with myObj.loadData(). You use $.when() to fetch the promise, and then simply chain .done() to deal with a successful call and .fail() to deal with the opposite :)
Here's your improved code:
var myObj = {
jsonEndPoint: '/test/test.json',
dataObjects: null
}
myObj.loadData = function () {
// We return the AJAX object so that we can evaluate the state later
// This is very simple :)
return $.ajax({
url: 'test.json',
type: 'POST',
dataType: 'json'
});
}()
myObj.displayData = function() {
// Instead of using the deprecated .success() and .error()
// ... we use .done() and .fail()
$.when(myObj.loadData).done(function(data) {
myObj.dataObjects = data.apiResults[0].league.season.draft.rounds[0].picks;
}).fail(function(xhr, textStatus, errorThrown) {
console.log('Data Load Error: ' + textStatus);
});
}()
If you are unsure, you can check the dummy code here: http://jsfiddle.net/teddyrised/5rbd2eqq/1/ I have used the built-in JSON response from JSfiddle to generate an artificial response, but the logic is exactly the same as yours.
Your displayData method is called even before the ajax is completed. So you either need to call displayData in the success callback of ajax or change the structure a bit so that its easy to call.
Why don't you instead do something like this
var myObj = {
jsonEndPoint: '/test/test.json',
dataObjects: null,
displayData: function() {
console.log(this.dataObjects);
},
loadData: function() {
$.ajax({
context: this,
url: 'test.json',
type: 'GET',
dataType: 'json',
success: function(data) {
this.dataObjects = data.apiResults[0].league.season.draft.rounds[0].picks;
console.log(myObj.dataObjects);
this.displayData();
},
error: function(xhr, textStatus, errorThrown) {
console.log('Data Load Error: ' + textStatus);
}
});
}
};
myObj.loadData();
Here is a demo

Variable Scope in Nested AJAX Calls

I have a set of custom user data that I want to make an ajax call to, and in the event that there is no user data, make another ajax call to retrieve a default set of data, and then execute a function after parsing the data. Here's an example:
var oData = [],
exampleUrl = 'example.php';
$.ajax({
url: exampleUrl + '?query=getUserData',
contentType: 'application/json;odata=verbose',
headers: {
'accept': 'application/json;odata=verbose'
},
success : function(data, request){
// Request succeeded
// Check the results
if(data.length){
// There are custom user results!
// Parse the results
oData = data;
}
else{
// There were no custom user results...
// Run another query to retrieve default values
$.ajax({
url: examplUrl + '?query=getDefaultData',
contentType: 'application/json;odata=verbose',
headers: {
'accept': 'application/json;odata=verbose'
},
success : function(data, request){
// Request succeeded
// Check the results
if(data.length){
// There was some default data!
// Parse the results
oData = data;
}
else{
// No data was found...
// Attempt to be helpful
console.log('No Default data was found!');
}
},
error : function(data, request){
// There was an error with the request
// Attempt to be helpful
console.log('Error retrieving data:');
console.log(data);
console.log(request);
}
});
}
},
error : function(data, request){
// There was an error with the request
// Attempt to be helpful
console.log('Error retrieving Custom User data:');
console.log(data);
console.log(request);
},
complete : function(){
// Do something with the data
index.displayData(oData);
}
});
The issue is that if the second ajax call is run, oData doesn't contain any data at all when it's passed to index.displayData(). I'm guessing it has something to do with the asyncronous nature of ajax calls, but shouldn't 'complete' run after everything inside of 'success' runs?
I also know I probably shouldn't be using the ajax "Pyramid of Doom" and should be using promises, but I've tried them and keep getting the same results.
Thank you for your assistance!
As pointed out by Violent Crayon, you could try calling "complete" yourself instead of relying on JQuery's implicit control flow:
function getData(exampleUrl, onComplete){
$.ajax({
success : function(data, request){
if(data.length){
onConplete(data);
}else{
$.ajax({
success : function(data, request){
if(data.length){
onComplete(data);
}else{
console.log('No Default data was found!');
}
},
error : function(data, request){
console.log('Error retrieving data:');
}
});
}
},
error : function(data, request){
console.log('Error retrieving Custom User data:');
}
});
}
var oData = [];
getData('example.php', function(data){
oData = data;
index.displayData(oData);
}
BTW, note how you can have your async functions receive their own return and error callbacks. This can help reduce the pyramid of doom problem without needing to use promises and without needing to hardcode the return callback.
By working with promises, you can avoid the need to pass a callback into your function, and by defining a utility function you can avoid repetition of code.
//reusable utility function, which returns either a resolved or a rejected promise
function fetchData(queryString, cache) {
return $.ajax({
url: 'example.php',
data: { query: queryString },
type: 'JSON',//assumed
cache: cache,
contentType: 'application/json;odata=verbose',
headers: { 'accept': 'application/json;odata=verbose' }
}).then(function(data, textStatus, jqXHR) {
if (data && data.length) {
return data;
} else {
return $.Deferred().reject(jqXHR, 'no data returned').promise();//emulate a jQuery ajax failure
}
});
}
This allows promise methods to be used for a control structure, which :
is concise
uses chaining, not nesting
gives meaningful error messages.
//control structure
fetchData('getUserData', false).then(null, function(jqXHR, textStatus) {
console.log('Error retrieving Custom User data: ' + textStatus);
return fetchData('getDefaultData', true);
}).then(index.displayData, function(jqXHR, textStatus) {
console.log('Error retrieving default data: ' + textStatus);
});
Notes :
the null in .then(null, function(){...}) allows a successful response to drop straight through to the second .then(index.displayData, ...)
default data is cached while the user data is not. This is not necessary to make things work but will be faster next time the default data is required.
in the world of promises, this or something similar is the way to go.

JSON pass null value to MVC 4 controller in IE9

I got some problem while posting JSON data into MVC 4 controller.
Below method is working fine in Firefox but unfortunately failed in IE 9
The JavaScript :
var newCustomer = {
CustName: $("#CustName").val(),
CustLocalName: $("#CustLocalName").val(),
CustNumber: $("#CustNumber").val(),
CountryID: $("#SelectCountry").val(),
City: $("#City").val()
};
$.ajax({
url: '#Url.Content("~/CustomerHeader/CreateCustomerHeader")',
cache: false,
type: "POST",
dataType: "json",
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(newCustomer),
success: function (mydata) {
$("#message").html("Success");
},
error: function () {
$("#message").html("Save failed");
}
});
and this is my controller :
public JsonResult CreateCustomerHeader(CustomerHeader record)
{
try
{
if (!ModelState.IsValid)
{
return Json(new { Result = "ERROR", Message = "Form is not valid! Please correct it and try again." });
}
RepositoryHeader.Update(record);
return Json(new { Result = "OK", Record = record});
}
catch (Exception ex)
{
return Json(new { Result = "ERROR", Message = ex.Message });
}
}
the "data" variable as in public JsonResult CreateCustomerHeader(CustomerHeader **data**) is getting NULL but while using FireFox it holds the correct value.
UPDATE : New method trying using $.post
function CreateNewCustomer(newCustomer) {
$.post("/CustomerHeader/CreateCustomerHeader",
newCustomer,
function (response, status, jqxhr) {
console.log(response.toString())
});
}
Based off the bit that you've shown, this is a simplified variation that may work more consistently, using jQuery.post() (http://api.jquery.com/jQuery.post/):
var data = {
CustName: $("#CustName").val(),
CustLocalName: $("#CustLocalName").val(),
CustNumber: $("#CustNumber").val(),
CountryID: $("#SelectCountry").val(),
City: $("#City").val()
};
$.post({
'#Url.Action("CreateCustomerHeader", "CustomerHeader")',
data,
function(response, status, jqxhr){
// do something with the response data
}).success(function () {
$("#message").html("Success");
}).error(function () {
$("#message").html("Save failed");
});
$.post() uses $.ajax as it's base, but abstracts some of the details away. For instance, $.post calls are not cached, so you don't need to set the cache state (and setting it is ignored if you do). Using a simple JavaScript object lets jQuery decide how to serialize the POST variables; when using this format, I rarely have issues with the model binder not being able to properly bind to my .NET classes.
response is whatever you send back from the controller; in your case, a JSON object. status is a simple text value like success or error, and jqxhr is a jQuery XMLHttpRequest object, which you could use to get some more information about the request, but I rarely find a need for it.
first of all I would like to apologize #Tieson.T for not providing details on JavaScript section of the view. The problem is actually caused by $('#addCustomerHeaderModal').modal('hide') that occurred just after ajax call.
The full script :
try{ ..
var newCustomer =
{
CustName: $("#CustName").val(),
CustLocalName: $("#CustLocalName").val(),
CustNumber: $("#CustNumber").val(),
CountryID: $("#SelectCountry").val(),
City: $("#City").val()
};
$.ajax({
url: '/CustomerHeader/CreateCustomerHeader',
cache: false,
type: "POST",
dataType: "json",
data: JSON.stringify(newCustomer),
contentType: "application/json; charset=utf-8",
success: function (mydata) {
$("#message").html("Success");
},
error: function () {
$("#message").html("Save failed");
}
});
}
catch(Error) {
console.log(Error.toString());
}
//$('#addCustomerHeaderModal').modal('hide')//THIS is the part that causing controller cannot retrieve the data but happened only with IE!
I have commented $('#addCustomerHeaderModal').modal('hide') and now the value received by controller is no more NULL with IE. Don't know why modal-hide event behave like this with IE9.
Thanks for all the efforts in solving my problem guys :-)

Can I evaluate the response type of an $.ajax() call in success callback?

I am using jQuery to make an AJAX request to a remote endpoint. That endpoint will return a JSON object if there is a failure and that object will describe the failure. If the request is successful it will return HTML or XML.
I see how to define the expected request type in jQuery as part of the $.ajax() call. Is there a way to detect the request type in the success handler?
$.ajax(
{
type: "DELETE",
url: "/SomeEndpoint",
//dataType: "html",
data:
{
"Param2": param0val,
"Param1": param1val
},
success: function(data) {
//data could be JSON or XML/HTML
},
error: function(res, textStatus, errorThrown) {
alert('failed... :(');
}
}
);
Have you application generate correct Content-Type headers (application/json, text/xml, etc) and handle those in your success callback. Maybe something like this will work?
xhr = $.ajax(
{
//SNIP
success: function(data) {
var ct = xhr.getResponseHeader('Content-Type');
if (ct == 'application/json') {
//deserialize as JSON and continue
} else if (ct == 'text/xml') {
//deserialize as XML and continue
}
},
//SNIP
);
Untested, but it's worth a shot.
how about using the complete option?
$.ajax({
...
complete : function(xhr, status) {
// status is either "success" or "error"
// complete is fired after success or error functions
// xhr is the xhr object itself
var header = xhr.getResponseHeader('Content-Type');
},
...
});
By the time it calls your success handler, the data has already been deserialized for you. You need to always return the same data type for any successful result. If there truly is an error, you should probably throw an exception and let it get handled by the error callback instead. This should be able to parse the resulting error and package it for your callback, that is, it will detect that the response did not have 200 OK status and parse the result to obtain the error information.

Categories