Apologies if this is a duplicate question, I've followed some steps from another question which didn't seem to help me. I am trying to retrieve some JSON data, store part of the data into a variable and use that variable in a separate function outside of the AJAX request.
My expected response from the json data is http://localhost:8000/tutorials/retrieve/?page=2 (This response shows if I log the variable inside of the AJAX code) however the actual response I get when I try to log the variable from another function is as follows:
n.Event {originalEvent: MouseEvent, type: "click", timeStamp: 1436727171161, jQuery21304066238570958376: true, toElement: div#loadmore.recentTutorials…}
Here is the current code
var recentFirstPage = '';
function retrieveTutorials(){
$.ajax({
type: "GET",
url: "/tutorials/retrieve",
dataType: "json",
success: function(data){
**some unrelated parsing code here**
//Set the variable to what I need
recentFirstPage = data.next_page_url;
},
error: function() {
alert("An error occurred processing AJAX request.");
}
});
}
$('#main-content-wrap').on('click', '.recentTutorials', function(recentFirstPage){
//Does not return expected result
console.log(recentFirstPage);
});
When I click .recentTutorials I expect the console to log the data from JSON however it doesn't. Can someone help clear up my error(s)?
The reason that it doesn't log the data from JSON s that the call is asynchronous. This means that the function will execute top to bottom without waiting for the call to finish.
One method that's used is to leverage deferred objects which return a promise on completion. You can accept an anonymous function to the invoker function so that it's call back is executed within the scope of the click.
Observe:
function retrieveTutorials(){
return $.ajax({
type: "GET",
url: "/tutorials/retrieve",
dataType: "json"
});
}
$('#main-content-wrap').on('click', '.recentTutorials', function(){
//store our function call as an ajax promise
var promise = retrieveTutorials();
//wait for the ajax to return so we can mutate the data
promise.done(function(data){
//now our data will be properly
recentFirstPage = data.next_page_url;
});
});
It seems to me that you are trying to log the data before the ajax is completed. It`s better to use deferreds . Try this:
function retrieveTutorials(){
return $.ajax({ // will return deferred object
type: "GET",
url: "/tutorials/retrieve",
dataType: "json",
success: function(data){
**some unrelated parsing code here**
//Set the variable to what I need
recentFirstPage = data.next_page_url;
},
error: function() {
alert("An error occurred processing AJAX request.");
}
});
}
$.when( retrieveTutorials() ).done(function ( data ) {
console.log(recentFirstPage);
});
The parameter in your click handler is the last and final nail in your coffin. It's always the jquery event and you shouldn't handle it at all.
You do need to call the retrieveTutorials() function within the handler and you need to pass it a callback function that will be executed on success. So your retrieveTutorials() function will look something like this:
function retrieveTutorials(success){
$.ajax({ type: "GET", url: "/tutorials/retrieve",
dataType: "json",
success: success,
error: function() { alert("An error occurred processing AJAX request.");
} }); }
And your click handler:
$('#main-content-wrap').on('click', '.recentTutorials', function(){
retrieveTutorials(function(data){
console.log(data.next_page_url);
});
});
You can also use all the promise based goodness in the other anwers, but the above would be an idiom you'll see again and again.
Related
I have a function that goes to a PHP script which returns the Server Operating System.
The script is literally dead simple:
<?php
echo (strpos(PHP_OS, 'Linux') > -1 ? 'Lin' : 'Win');
My goal is to be able to differentiate between operating systems so that I can declare a global path variable in my .js file for future uses.
This is what I've done so far:
function serverOS()
{
var os;
$.ajax({
url: '../scripts/ajax/detect-os.php',
type: 'get',
success: function(res)
{
os = res;
return os;
},
error: function(res) {alert('Major Error!'); console.log(res)}
});
return os;
}
console.log(serverOS());
The ending console.log outputs undefined - but if I console.log os inside of the success callback function, then it outputs what I expect.
According to this: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var
I should be able to do what I want with the above script but it doesn't seem to work. How do I go about setting and getting a global variable using ajax in JavaScript/jQuery?
AJAX operations are asynchronous. They will not block the rest of your JavaScript from executing.
The final return statement in your function attempts to return os immediately (before the AJAX operation has completed. Remove that return statement and in the success handler take care of all the logic to get the value back to the caller.
function serverOS() {
// The AJAX method will invoke the logging function no matter what.
// But, it won't happen until the AJAX call is complete.
$.ajax({
url: '../scripts/ajax/detect-os.php',
type: 'get',
success: function(res) {
returnValue(res);
},
error: function(res) {
alert('Major Error!');
returnValue(res);
}
});
}
function returnValue(val){
console.log(val);
}
serverOS();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Scott's Answer definitely works - but there does also seem to be an alternative I stumbled across. There's an AJAX property called async. Setting this to false in my function means it becomes a synchronous ajax call. Changing my function to this:
var os;
function serverOS()
{
$.ajax({
url: '../scripts/ajax/detect-os.php',
type: 'get',
async: false,
success: function(res)
{
returnValue(res)
},
error: function(res)
{
alert('Major Error!');
returnValue(res)
}
});
}
function returnValue(val)
{
os = val;
return os;
}
serverOS();
console.log(os); //this print Lin on my Linux machine.
ref: https://api.jquery.com/jQuery.ajax/
I usually like to organize my code so that one function fires a bunch of other
functions, like this:
/**
* GET MESSAGES:
*/
$(function() {
$.ajax({
url: '/messages',
method: 'GET',
dataType: 'json',
success: function(messages) {
if (messages.length > 0) {
keyedMessages = keyFork(messages);
reversedMessages = reverse(keyedMessages);
crushedMessages = crush(reversedMessages);
getFriendships(messages, crushedMessages);
}
mail.template.airmail();
}
});
});
However, if I need to do a second Ajax request inside one of the nested
functions I can't return the data because of the scope of the Ajax request
and it makes my code inconsistent and hard to follow, sort of broken up all over the place. For example, if one of the functions
invoked above fires a second Ajax request for friendships anything I write
after that will be broken from the communication chain due to the request and it seems impossible to return anything:
/**
* GET FRIENDSHIPS:
*/
function getFriendships(messages, crushedMessages) {
$.ajax({
url: 'friendships',
method: 'get',
dataType: 'json',
success: function(friendships) {
addKey(crushedMessages, friendships);
filteredCrushedMessages = filterUnconfirmedSender(crushedMessages);
filteredCrushedMessages.forEach(function(filteredCrushedMessage) {
mail.sidebar.builder.messengers(filteredCrushedMessage);
});
mail.snailMail.onload();
}
});
}
If I try to return the data it doesn't work. Consequently I'll have to
continue invoking functions inside the nested request, every time I need to make another nested ajax request it breaks the chain. This makes my
code very hard to read. Are there any solutions to this problem or is
code that uses Ajax requests just hard to read?
You could store the data on a DOM element, then use jQuery Custom Events to get it done.
There's even support for passing arguments to your event handler:
https://learn.jquery.com/events/introduction-to-custom-events/#naming-custom-events
If I try to return the data it doesn't work.
Not appear jQuery promise returned from either function at Question ?
Try utilizing return statement , $.when.apply(this, arrayOfPromises) to return array of jQuery promise object from getFriendships
function getFriendships(messages, crushedMessages) {
return $.ajax({
url: 'friendships',
method: 'get',
dataType: 'json',
success: function(friendships) {
addKey(crushedMessages, friendships);
filteredCrushedMessages = filterUnconfirmedSender(crushedMessages);
mail.snailMail.onload();
return $.when.apply($
, filteredCrushedMessages.map(function(filteredCrushedMessage) {
return mail.sidebar.builder.messengers(filteredCrushedMessage);
})
);
}
});
}
// e.g.,
getFriendships(messages, crushedMessages)
.then(function success() {
console.log(arguments)
}, function err(jqxhr, textStatus, errorThrown) {
console.log(errorThrown)
})
Method for handling ajax
function ajaxMethod(){
return $.ajax({
url: ajaxUrl,
type: "POST",
dataType: "JSONP",
jsonpCallback: ajaxCallback
});
}
Calls to this method:
dD(ajaxMethod());
aA(ajaxMethod());
bB(ajaxMethod());
cC(ajaxMethod());
aa,bb,cc,dd method have
promise.success(function (response) {
console.log(response);
});
Now aA response is coming in bB function,,bb response in cc function and as simultaneous call is coming.
Also tried using async true nothing happens.
Any suggestions?
With jsonpCallback you are telling jQuery to use a specific function name for the callback function (instead of generating a unique one), so each time you call it, you overwrite the previous function.
Just remove jsonpCallback: ajaxCallback.
While you are at it, remove type: "POST",, it is incompatible with JSONP.
I think this is what you are after.
This code is using the returned promise to wait for the result then passing the result to your other function.
ajaxMethod().success(function(response)
{
dD(response);
});
ajaxMethod().success(function(response)
{
aA(response);
});
ajaxMethod().success(function(response)
{
cC(response);
});
ajaxMethod().success(function(response)
{
dD(response);
});
Your aA, bB, cC and dD methods can now be:
function <insertname>(response)
{
console.log(response);
}
If you want your request to come in the synchronous way, then try the following :
var callBack = $.Callbacks();
callBack.add(dD(ajaxMethod()));
callBack.add(aA(ajaxMethod()));
callBack.add(bB(ajaxMethod()));
callBack.add(cC(ajaxMethod()));
callBack.fire();
the above line of code will make sure the respective ajax call would get call.
Below is a function that is called when a user clicks the "Save" button on a form. When the PreSaveAction function returns true, the form will be submitted. If false is returned, nothing will happen. I'm using an AJAX call to here to validate form values and would like to have PreSaveAction return true if validateUniqueStaff succeeds, and vise versa if it fails.
function PreSaveAction() {
$.ajax({
url: listUrl,
type: "GET",
dataType: "json",
success: validateUniqueStaff,
error: function (data) {
alert("Error: Problem with the AJAX request");
}
});
//if (validateUniqueStaff succeeds) return true, else return false
}
My trouble is that I can't figure out how to incorporate a deferred object here. I've tried running the ajax call synchronously instead, which works in Chrome, but not in IE8 (a requirement).
I'm absolutely stumped. Any advice would be hugely appreciated! Let me know if I can provide any other information.
It is actually possible!
You need to use the developer tools (F12) to get the current java script code behind the button.
Here's how to proceed next:
//1. unbind the current handler:
var saveButton = ...; //your id (looks like ctl00_***)
var saveButtonCallBack = ...; //call back id (looks like ctl00$...)
//2. When document is ready
jQuery(saveButton).unbind('click').click(function(){
PreSaveActionPeter(
function (){
WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions(saveButtonCallBack, "", true, "", "", false, true))
}
)
});
//3. Define your function:
function PreSaveActionPeter(callBack)
{
jQuery.ajax({
url: *your url*,
method: "GET",
headers: {
"accept": "application/json;odata=verbose",
},
dataType: "json",
success: function (data) {
if (data.d.results.length > 0) {
callBack();
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert(XMLHttpRequest.responseText);
}
});
return true;
}
//4. Override of the default PreSaveAction
function PreSaveAction()
{
return false;
}
Your PreSaveAction() is asynchronous. You can't return a success or failure value from that function. It will return long BEFORE the ajax call has completed.
For asynchronous calls (such as Ajax calls), you HAVE to process the result in a callback that gets called sometime later. You can't pretend to use synchronous coding techniques with an asynchronous operation. It just won't work. PreSaveAction() returns long before the ajax call is done so it's return value will not know the end result of the Ajax call.
If your SharePoint environment requires PreSaveAction() to return true/false and you need to do an Ajax operation in order to figure out if you should return true or false, then you're in a bind. You could make the Ajax call be synchronous, but that's generally a horrible user experience because it locks up the browser during the Ajax call and if you're going cross domain, you can't do synchronous anyway.
The best solution would be to understand what your options are with the PreSaveAction() function and see if you can pass a callback into it that will be called with the final result when the asynchronous Ajax call is done.
That would work something like this:
function PreSaveAction(callback) {
$.ajax({
url: listUrl,
type: "GET",
dataType: "json",
success: function(data) {
var retVal = validateUniqueStaff(data);
// call the callback to report the results of validation
callback(retVal);
},
error: function (data) {
alert("Error: Problem with the AJAX request");
callback(false);
}
});
}
The little bit of searching I did on PreSaveAction() in SharePoint indicates that it has to be synchronous. It was designed for client-side validation only (without Ajax calls to the server). To use it, you will have to return true or false directly from it and thus you can't use asynchronous Ajax calls in your PreSaveAction() implementation because the result won't be known in time. You might be able to make your Ajax call synchronous (which I hate because it can be a bad user experience) or you need to find a different way to do your server-assisted validation.
For form validation, it has been standard practise since time immemorial to issue form.submit() from a submit handler on successful validation, and to return false unconditionally.
Presumably, in the SharePoint environment, you can do the same but with the added minor convolution, in this case, that validation be undertaken only after successful receipt of an AJAX response.
Exactly what you write depends on what your validateUniqueStaff returns.
Assuming validateUniqueStaff to return a boolean (valid/invalid), you would write:
function PreSaveAction() {
var form = this;
//disable Save button here
$.ajax({
url: listUrl,
type: "GET",
dataType: "json"
}).then(validateUniqueStaff, function(jqXHR, textStatus, errorThrown) {
return new Error(textStatus);
}).then(function(valid) {
if(valid) {
form.submit();
} else {
return $.Deferred.reject(new Error('Validation failure')).promise();
}
}).fail(function(e) {
alert(e.message);//this will be either the AJAX error message or the validation failure message
}).always(function() {
//re-enable Save button here
});
return false;
}
Or (more elegantly from PreSaveAction's point of view), assuming validateUniqueStaff to return a Promise that is resolved on success or rejected on failure, you would write :
function PreSaveAction() {
var form = this;
//disable Save button here
$.ajax({
url: listUrl,
type: "GET",
dataType: "json"
}).then(validateUniqueStaff, function(jqXHR, textStatus, errorThrown) {
return new Error(textStatus);
}).then(form.submit, function(e) {
alert(e.message);//this will be either the AJAX error message or the validation failure message
}).always(function() {
//re-enable Save button here
});
return false;
}
I really don't think you should be considering synchronous AJAX, which is as unreliable as it is unnecessary.
I am practicing jquery promises and something strange is happening in my demo code. What my code does is when I click on button then it receives a simple json data from the server. I am using two jquery promises one is done() and second is fail(). Whne I click on button it receives data from the server but the done() is not executing the data is shown in the console via fail(). Why and how can I solve this? Following is my code
jquery
var Obj = function () {
return {
gets: function (successHandler, errorHandler) {
console.log('hello');
return $.ajax({
url: '/server.php',
dataType: 'JSON',
type: 'GET'
});
}
}
};
$('.button').on('click', function () {
var obj = new Obj();
var promise = obj.gets();
promise.done(function (data) {
console.log(data);
});
promise.fail(function (e) {
console.log(JSON.stringify(e)); //this logs below
});
output
{"readyState":4,"responseText":"<?php\n$response = array('oranges', 'apples', 'berries');\nexit(json_decode($response));","status":200,"statusText":"OK"}
here is my php code
$response = array('oranges', 'apples', 'berries');
exit(json_decode($response));
return $.ajax({
url: '/server.php'
dataType: 'JSON',
...
BUT
"responseText":"<?php\n$response = array ...
jQuery expects JSON but the server delivers the PHP source. So an parsing exception is thrown, the Deferred is rejected and .fail() is called.