I need to combine three methods:
An AJAX to check the existence of a code
If the code exists, confirm whether to overwrite
Overwrite
I've written three methods that return a $.Deferred in order to chain them together with .done(), which are below:
function checkFunction() {
var code = $("#code").val();
return $.ajax({
url: "${pageContext.request.contextPath}/dataManagement/codeMaintenance/check",
method: "POST",
async: false,
data: {
"reasonCode": code
},
success: function(response, textStatus, jqXHR) {
var exists = response.dataMap.exists;
console.log("Code exists: " + exists);
if (exists == true) {
return $.Deferred().resolve(true);
} else {
return $.Deferred().reject();
}
}, error: function() {
return $.Deferred().reject("AJAX ERROR");
}
});
};
var confirmFunction = function(codeExists) {
console.log("Confirming overwrite");
if (codeExists == true) {
var confirm = confirm("Code Exists: Do you wish to overwrite?");
if (confirm == true) {
return $.Deferred(true);
} else {
return $.Deferred(false);
}
} else {
return $.Deferred(true);
}
};
var saveFunction = function() {
console.log("Saving");
var code = $("#code").val();
return $.ajax({
url: "${pageContext.request.contextPath}/dataManagement/codeMaintenance/save",
method: "POST",
data: {
"reasonCode": code
},
success: function(response, textStatus, jqXHR) {
alert("test");
return $.Deferred(true);
}
});
};
I then attempt to execute via this line:
checkFunction().done(confirmFunction(codeExists)).done(saveFunction());
Unfortunately, the parameter I set on the $.Deferred from the first method does not get passed as a parameter to confirmFunction().
What am I doing wrong?
Jason
In short: plenty.
You try to use return inside of asynchronous functions in the success handlers of your $.ajax() calls.
Here you pass the result of the function call and not a reference of the function as callbacks:
checkFunction().done(confirmFunction(codeExists)).done(saveFunction());
This should be more like this:
checkFunction().done(confirmFunction).done(saveFunction);
In confirmFunction() you return a new Deferred object. What you should do is create a Deferred object, return the respective promise and then resolve/reject the Deferred object. So, e.g., your checkFunction() function should look like this:
function checkFunction() {
var code = $("#code").val();
// create deferred object
var result = $.Deferred();
return $.ajax({
url: "${pageContext.request.contextPath}/dataManagement/codeMaintenance/check",
method: "POST",
async: false,
data: {
"reasonCode": code
},
success: function(response, textStatus, jqXHR) {
var exists = response.dataMap.exists;
console.log("Code exists: " + exists);
if (exists == true) {
result.resolve(true);
} else {
result.reject();
}
}, error: function() {
result.reject("AJAX ERROR");
}
});
return result.promise();
}
Related
i'm trying to get my head around working with Deffered objects especially in cases where you have to perform multiple asynchronous operations on every item in an array. In the code below i just want to be able to access the result of an asynchronous array after it is complete, in my case it is the results array.
To explain the code below
ListData function derives the source data which is an array that i intend to manipulate.
getPictureComplete1 perfroms an async operation (ListDataWithPicture) on every item in the array above
The idea of step 2 was to add an image url to every item in the array from Step 1 and then use the new array as an input to step 4
could be to print the images to the page or perform additional manipulations on the array
var mydeferred = $.Deferred();
var ListData = function (){
listName = 'TeamInfo';
$.ajax({
url: _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getbytitle('"+listName+"')/items?$select=Name/Title,Name/Name,Name/Id,Name/EMail,Name/WorkPhone&$expand=Name/Id",
type: "GET",
headers: { "ACCEPT": "application/json;odata=verbose" },
success: onQuerySucceded,
error: onQueryFailed
});
return mydeferred.promise();
}
var ListDataWithPicture = function(userId, callback) {
// execute AJAX request
$.ajax({
url: _spPageContextInfo.siteAbsoluteUrl + "/_api/web/SiteUserInfoList/items?$filter=Id eq " + userId + "&$select=Picture",
type: "GET",
async: false,
headers: { "ACCEPT": "application/json;odata=verbose" },
success: function(data){
console.log("Starting async operation for " + userId);
var pictureLink = "";
var mydata = callback(data.d.results[0].Picture.Url);
return mydata
},
error: onQueryFailed
});
}
function onQuerySucceded (data){
var PeopleCompleteList = [];
for (i=0; i< data.d.results.length; i++) {
//check if the user exists if he does store the following properties name,title,workphone,email
if(data.d.results[i]['Name'] != null){
personName = data.d.results[i]['Name'].Name.split('|')[2];
userName = data.d.results[i]['Name']['Name'];
UserTitle = data.d.results[i]['Name']['Title'];
UserphoneNumber = data.d.results[i]['Name']['WorkPhone'];
UserEmail = data.d.results[i]['Name']['EMail'];
Id = data.d.results[i]['Name']['Id'];
PeopleCompleteList.push(PersonConstructor(personName, UserTitle, UserphoneNumber,UserEmail,Id));
}
}
mydeferred.resolve(PeopleCompleteList);
}
function getPictureComplete1 (data){
var def = new $.Deferred();
var results = [];
var expecting = data.length;
data.forEach(function(entry, index) {
//this is the asynchronous function
ListDataWithPicture(entry.UserId, function(result) {
results[index] = {imageUrl: result, UserId: entry.UserId, name: entry.name, Title: entry.Title, phoneNumber: entry.phoneNumber, Email: entry.Email};
//console.log(result);
if (--expecting === 0) {
// Done!
console.log("Results:", results); //this works succeffully from here
def.resolve();
return results
//mydeferred.resolve(results);
}
});
});
return mydeferred.promise();
}
$(function () {
ListData().then(function(data){
//how can i access the results array in this function after it has completed??
var value = getPictureComplete1 (data);
//the line below results undefined,which i understand because the getPictureComplete1 function may not have completed at the time
console.log(value);
});
Because this is an asynchronous operation, the only time that you have access to the array of results from your ajax call in LineData is within the scope of the onQuerySucceded function that you assigned to the success property in your ajax configuration. The function you define here will be fired after a successful response was received.
When establishing a deferred promise, you will need to define what data resolves the promise. Whatever you pass into the resolve method will be available as a parameter within a subsequent then block on your promise chain.
I'm not seeing onQuerySucceded defined in your example, but it would look something like this:
var ListData = function (){
var mydeferred = $.Deferred();
listName = 'TeamInfo';
$.ajax({
url: _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getbytitle('"+listName+"')/items?$select=Name/Title,Name/Name,Name/Id,Name/EMail,Name/WorkPhone&$expand=Name/Id",
type: "GET",
headers: { "ACCEPT": "application/json;odata=verbose" },
success: function onQuerySuccess(data) {
mydeferred.resolve(data);
},
error: onQueryFailed
});
return mydeferred.promise();
}
Now, after a successful call, the promise will resolve with the data. So, something like this is possible:
ListData()
.then(function (data) {
// do something with the data
})
Similarly, you will want to define what are the rejection cases for your deferred promise. For example, maybe the ajax call doesn't succeed. For this, you will want to use the reject method on the deferred object.
For example:
var ListData = function (){
var mydeferred = $.Deferred();
listName = 'TeamInfo';
$.ajax({
url: _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getbytitle('"+listName+"')/items?$select=Name/Title,Name/Name,Name/Id,Name/EMail,Name/WorkPhone&$expand=Name/Id",
type: "GET",
headers: { "ACCEPT": "application/json;odata=verbose" },
success: function onQuerySuccess(data) {
mydeferred.resolve(data);
},
error: function onQueryFailed(error) {
mydeferred.reject(error);
}
});
return mydeferred.promise();
}
This works similarly to resolve, but will resolve to any subsequent catch or fail blocks. So, something like this will work
LineData()
.then(function (data) {
// it was successful, do something with the data
})
.catch(function (error) {
// There was an error. The then block was not called.
// Do something with the error.
})
Put even more simply, you can set mydeferred.resolve and mydeferred.reject as these properties directly. Like so:
var ListData = function (){
var mydeferred = $.Deferred();
listName = 'TeamInfo';
$.ajax({
url: _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getbytitle('"+listName+"')/items?$select=Name/Title,Name/Name,Name/Id,Name/EMail,Name/WorkPhone&$expand=Name/Id",
type: "GET",
headers: { "ACCEPT": "application/json;odata=verbose" },
success: mydeferred.resolve,
error: mydeferred.reject
});
return mydeferred.promise();
}
You will want to do something similar with your ListDataWithPicture function so that it also returns a promise with the data that you need:
var ListDataWithPicture = function(userId, callback) {
var deferred = $.Deferred();
$.ajax({
url: _spPageContextInfo.siteAbsoluteUrl + "/_api/web/SiteUserInfoList/items?$filter=Id eq " + userId + "&$select=Picture",
type: "GET",
async: false,
headers: { "ACCEPT": "application/json;odata=verbose" },
success: function(data){
deferred.resolve(data.d.results[0].Picture.Url)
},
error: deferred.reject
});
return deferred.promise();
}
This allows you to do something like this:
LineData()
.then(function (data) {
// performing on just the first result:
return ListDataWithPicture(data[0]);
})
.then(function (url) {
// do something with the result
})
.catch(function (error) {
// do something with the error
});
Because you want to perform a aynchronous operation on each of the items in your array, I'd recommend to use Promise.all which executes and resolves an array of promises.
LineData()
.then(function (data) {
// create a map of promises
var promises = data.map(function (item) {
return ListDataWithPicture(item);
});
return Promise.all(promises);
})
.then(function (urls) {
// urls will be an array of urls resolved from calling
// ListDataWithPicture on each item in the array resolved
// from above
})
.catch(function (error) {
// do something with the error
});
Here are some good resources to learn more:
http://api.jquery.com/category/deferred-object/
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
I have a function in JS & jQuery that fires an AJAX call and it has a callback block to let me know when it's finished:
function ajaxCall(url, type, dataType, dataToSend, callback) {
if (dataType == undefined) dataType = "json";
if (dataToSend == undefined) dataToSend = null;
$.ajax({
url: url,
type: type,
dataType: dataType,
contentType: "application/json",
data: dataToSend,
async: true,
success: function (result) {
callback(result);
},
error: function (data, status) {
console.error("Server Error: " + status);
}
});
}
I am accessing it like so, but using external functions like showAjaxLoader() just doesn't work! it says this function is undefined:
function registerUser(data) {
ajaxCall(pathServiceRegister, "POST", undefined, JSON.stringify(data), function (result) {
// SOME CODE THAT RUNS WHEN IT'S COMPLETE
// External method:
showAjaxLoader(false); // Doesn't work
});
});
function showAjaxLoader(show) {
var loader = $('.ajax-loader');
if (show) {
loader.fadeIn("fast");
} else {
loader.fadeOut("fast");
}
}
What am I doing wrong?
Thanks :)
Worked out some sample. this may be good practice. Try this :
$(document).ready(function() {
$("button").click(function() {registerUser();});
});
var Scallback = function(arg) {
alert("Success :"+arg);
showAjaxLoader(true);
}
var Ecallback = function(arg) {
alert("Err :"+arg);
showAjaxLoader(true);
}
function showAjaxLoader(show) {
var loader = $('.ajax-loader');
if (show) {
loader.fadeIn("fast");
} else {
loader.fadeOut("fast");
}
}
function ajaxCall(url, type, Scallback, Ecallback) {
$.ajax({
url : url,
type : type,
async : true,
success : function(result) {
Scallback(result);
},
error : function(data) {
Ecallback(data)
}
});
}
function registerUser()
{
ajaxCall(pathServiceRegister, "GET", Scallback, Ecallback);
}
Have you tried to do something like:
var that = this;
function registerUser(data) {
ajaxCall(pathServiceRegister, "POST", undefined, JSON.stringify(data), function (result) {
// SOME CODE THAT RUNS WHEN IT'S COMPLETE
// External method:
that.showAjaxLoader(false);
});
});
Declare your method like this
var obj = {
showAjaxLoader : function(show) {
var loader = $('.ajax-loader');
if (show) {
loader.fadeIn("fast");
} else {
loader.fadeOut("fast");
}
}
}
Then inside ajax, call obj.showAjaxLoader(false); This may work.
I want to execute a callback function inside an object. I don't know if there is something wrong in the way I'm doing this.
I've googled for a solution, also searched on stackoverflow but couldn't find anything similar to the way I'm coding this.
PHPGateway.js
var PHPGateway = {
opt_friendlyURL: true,
opt_folder: 'ajax/',
callback_function: null,
useFriendlyURL: function (bool) {
this.opt_friendlyURL = bool;
},
setFolder: function (folder) {
this.opt_folder = folder;
},
send: function (service, method, data, callback) {
var url,
json_data = {};
if (this.opt_friendlyURL) {
url = this.opt_folder + service + '/' + method;
} else {
url = this.opt_folder + 'gateway.php?c=' + service + '&m=' + method;
}
if (data != undefined) {
json_data = JSON.stringify(data);
}
this.callback_function = (callback == undefined) ? null : callback;
$.ajax({
method: 'POST',
url: url,
data: {data: json_data},
success: this.ajax_success,
error: this.ajax_error
});
},
ajax_success: function (returned_object) {
if (this.callback_function != null) {
this.callback_function(returned_object.error, returned_object.data);
}
},
ajax_error: function () {
this.callback_function.call(false, {});
}
};
Then inside the HTML file that loads PHPGateway.js, I've the following code:
<script>
function submit_handler(event) {
event.preventDefault();
form_submit();
}
function form_callback(error, data) {
if(error == null) {
alert(data.text);
}
}
function form_submit() {
var data = {
status: $('#inStatus').val(),
amount: $('#inAmount').val(),
id: $('#inBudgetID'). val()
}
PHPGateway.send('budget', 'status', data, form_callback);
}
$('form').one('submit', submit_handler);
</script>
I get an error on this.callback_function(returned_object.error, returned_object.data);, the error is Uncaught TypeError: Object # has no method 'callback_function'.
What am I doing wrong?
Is this the best way to do it?
Thank You!
Based on minitech answer, I've updated PHPGateway.js like this. I've omitted the parts that weren't updated.
var PHPGateway = {
// Omitted code
send: function (service, method, data, callback) {
var url,
json_data = {},
that = this;
if (this.opt_friendlyURL) {
url = this.opt_folder + service + '/' + method;
} else {
url = this.opt_folder + 'gateway.php?c=' + service + '&m=' + method;
}
if (data != undefined) {
json_data = JSON.stringify(data);
}
this.callback_function = (callback == undefined) ? null : callback;
$.ajax({
method: 'POST',
url: url,
data: {data: json_data},
success: function(data, textStatus, jqXHR) {
that.ajax_success(data, textStatus, jqXHR);
},
error: function(jqXHR, textStatus, errorThrown) {
that.ajax_error(jqXHR, textStatus, errorThrown);
}
});
},
ajax_success: function (data, textStatus, jqXHR) {
if (this.callback_function != null) {
this.callback_function(true, data.data);
}
},
ajax_error: function (jqXHR, textStatus, errorThrown) {
this.callback_function.call(false, {});
}
};
Now it works!!!
In your call to $.ajax, you need to add a context option:
$.ajax({
method: 'POST',
url: url,
data: {data: json_data},
context: this,
success: this.ajax_success,
error: this.ajax_error
});
Your this variable in your Ajax success and error handlers are not pointing to the object you think they are. The context option to $.ajax() sets which object this points to in the Ajax callbacks.
Here’s your problem:
$.ajax({
method: 'POST',
url: url,
data: {data: json_data},
success: this.ajax_success,
error: this.ajax_error
});
When you set success and error to methods on this, they don’t keep their this. When a JavaScript function is called, it gets bound a this:
someFunction(); // this is undefined or the global object, depending on strict
someObject.someFunction(); // this is someObject
The built-in .call, .apply, and .bind of Function objects help you override this.
In your case, I think jQuery binds this to the Ajax object – a good reason to both not use jQuery and always use strict mode.
If you can guarantee or shim ES5 support, bind is an easy fix:
$.ajax({
method: 'POST',
url: url,
data: {data: json_data},
success: this.ajax_success.bind(this),
error: this.ajax_error.bind(this)
});
Which is equivalent to this if you can’t:
var that = this;
$.ajax({
method: 'POST',
url: url,
data: {data: json_data},
success: function() {
that.ajax_success.apply(that, arguments);
},
error: function() {
that.ajax_error.apply(that, arguments);
}
});
And now, a tip for you: don’t namespace, and if you do, don’t use this. this is great for objects that are meant to be constructed. What would seem more appropriate is something like this, if you really have to:
var PHPGateway = (function() {
var callbackFunction;
var options = {
friendlyURL: true,
…
};
…
function send(service, method, data, callback) {
…
}
…
return { send: send };
})();
I have over 50 AJAX calls from different functions of my code. All these calls have a similar structure with different data/url/callback params:
var jqXHR = $.post('/dba/port.php', {
mode: "del_wallfunds",
pdata: cdata,
wname: wName
},
function (data) {}, "json")
.done(function (data) {
var msg = data.msg;
if (msg.indexOf("Error") == -1) {
alertify.success(msg);
delSelected(selGroup);
} else {
alertify.error(msg);
}
})
.fail(function () {
alertify.error("Error .....");
});
I am thinking how to write a function that would return that var jqXHR to minimize the total size of the code. It is not a problem to pass all static variables like URL, error strings etc. But the problem is that all callback functions on ".done" are different and I don't know how to pass these callback functions as variables.
One way would be to call a single "universal" function on .done and pass a "switch" variable to that function, but it doesn't seem to be an elegant solution.
Any suggestions how to it in some elegant way?
Thanks
Either pass the done callback function as an argument when calling your function:
function ajaxCall(url, data, doneCallback) {
return $.post(url, data, doneCallback, "json").fail(...);
// or
return $.post(url, data, function() {}, "json").done(doneCallback).fail(...);
}
var jqXhr = ajaxCall('yoururl.php', {key: 'value'}, function(data) {
// do something
});
Or return the jqXhr object from the function, and assign the done callback then:
function ajaxCall(url, data) {
return $.post(url, data, function() {}, "json").fail(...);
}
var jqXhr = ajaxCall('yoururl.php', {key: 'value'});
jqXhr.done(function(data) {
// do something
});
Alternatively switch to using jQuery.ajax() instead, and pass the entire options object in:
function ajaxCall(options) {
return $.ajax(options).fail(...);
}
var jqXhr = ajaxCall({
url: 'yoururl.php',
data: {key: 'value'},
dataType: 'json'
});
jqXhr.done(function(data) {
// do something
});
You can try to :
turn "request successfully returned a treatment error" into a "rejected request",
put the "alertify" processing in a common callback
Here is a sketch of what this could give :
function myAjaxApi(url, data){
var myAjaxCall = $.post(url, data, function (data) {}, "json")
.then(function (data) {
// using .then : change "request succesful with error state"
// to "rejected state"
var msg = data.msg;
if (msg !== undefined && msg.indexOf("Error") >= 0) {
var dfd = $.Deferred();
// try to match the same signature as the "error" option
dfd.reject(this, msg);
return dfd;
} else {
return data
}
});
myAjaxCall.done(function(data){
if (data.msg) {
alertify.success(data.msg);
}
}).fail(function (jqxhr, msg) {
if (!msg) { msg = "Error ....."; }
alertify.error(msg);
});
return myAjaxCall;
}
//usage
myAjaxApi('/dba/port.php', {mode: "del_wallfunds", pdata: cdata, wname: wName})
.done(function (data) {
// the ".done()" queue will not be executed if msg contains "Error" ...
delSelected(selGroup);
});
Some parts should be written with more care ; the above example is meant to illustrate how you can wrap your repeated ajax calls inside a common api.
I'm trying to create a global handler that gets called before the ajax success callback. I do a lot of ajax calls with my app, and if it is an error I return a specific structure, so I need to something to run before success runs to check the response data to see if it contains an error code bit like 1/0
Sample response
{"code": "0", "message": "your code is broken"}
or
{"code": "1", "data": "return some data"}
I can't find a way to do this in jQuery out of the box, looked at prefilters, ajaxSetup and other available methods, but they don't quite pull it off, the bets I could come up with is hacking the ajax method itself a little bit:
var oFn = $.ajax;
$.ajax = function(options, a, b, c)
{
if(options.success)
{
var oFn2 = options.success;
options.success = function(response)
{
//check the response code and do some processing
ajaxPostProcess(response);
//if no error run the success function otherwise don't bother
if(response.code > 0) oFn2(response);
}
}
oFn(options, a, b, c);
};
I've been using this for a while and it works fine, but was wondering if there is a better way to do it, or something I missed in the jQuery docs.
You can build your own AJAX handler instead of using the default ajax:
var ns = {};
ns.ajax = function(options,callback){
var defaults = { //set the defaults
success: function(data){ //hijack the success handler
if(check(data)){ //checks
callback(data); //if pass, call the callback
}
}
};
$.extend(options,defaults); //merge passed options to defaults
return $.ajax(options); //send request
}
so your call, instead of $.ajax, you now use;
ns.ajax({options},function(data){
//do whatever you want with the success data
});
This solution transparently adds a custom success handler to every $.ajax() call using the duck punching technique
(function() {
var _oldAjax = $.ajax;
$.ajax = function(options) {
$.extend(options, {
success: function() {
// do your stuff
}
});
return _oldAjax(options);
};
})();
Here's a couple suggestions:
var MADE_UP_JSON_RESPONSE = {
code: 1,
message: 'my company still uses IE6'
};
function ajaxHandler(resp) {
if (resp.code == 0) ajaxSuccess(resp);
if (resp.code == 1) ajaxFail(resp);
}
function ajaxSuccess(data) {
console.log(data);
}
function ajaxFail(data) {
alert('fml...' + data.message);
}
$(function() {
//
// setup with ajaxSuccess() and call ajax as usual
//
$(document).ajaxSuccess(function() {
ajaxHandler(MADE_UP_JSON_RESPONSE);
});
$.post('/echo/json/');
// ----------------------------------------------------
// or
// ----------------------------------------------------
//
// declare the handler right in your ajax call
//
$.post('/echo/json/', function() {
ajaxHandler(MADE_UP_JSON_RESPONSE);
});
});
Working: http://jsfiddle.net/pF5cb/3/
Here is the most basic example:
$.ajaxSetup({
success: function(data){
//default code here
}
});
Feel free to look up the documentation on $.ajaxSetup()
this is your call to ajax method
function getData(newUrl, newData, callBack) {
$.ajax({
type: 'POST',
contentType: "application/json; charset=utf-8",
url: newUrl,
data: newData,
dataType: "json",
ajaxSuccess: function () { alert('ajaxSuccess'); },
success: function (response) {
callBack(true, response);
if (callBack == null || callBack == undefined) {
callBack(false, null);
}
},
error: function () {
callBack(false, null);
}
});
}
and after that callback success or method success
$(document).ajaxStart(function () {
alert('ajax ajaxStart called');
});
$(document).ajaxSuccess(function () {
alert('ajax gvPerson ajaxSuccess called');
});