ajax complete callback function is never called - javascript

I'm using Django.
I have the following code
var done_cancel_order = function(res, status) {
alert("xpto");
};
var cancel_order = function() {
data = {};
var args = {
type:"GET",
url:"/exchange/cancel_order/"+this.id,
data:data,
complete:done_cancel_order
};
$.ajax(args);
return false;
};
The function var cancel_order is called when I press a button on the page. That url when accessed is does some things on the server side, which I can check indeed are done, and then returns a json specifying whether or not the request was successful. You get:
{'status':200, 'message':'order canceled'}
The problem is that the callback is never called. I would like to have the callback display to the user the thing that was returned from the server. But even the first alert("xpto") inside the callback is never executed. Why is that?
EDIT:
I have checked that this code:
var cancel_order = function() {
data = {};
var args = {
type:"GET",
url:"/exchange/cancel_order/"+this.id,
data:data,
complete: function() { alert("xpto"); }
};
$.ajax(args);
return false;
};
displays the same behavior as described above: everything goes great on the server side, but the callback isn't called.

Be sure nothing is messing with your debug tools [e.g. console.log], it may end up wrecking your js code, delivering unexpected results.

Why don't you change it to this:
function done_cancel_order (res, status) {
/* remains same */
};
I hope, this one would work for you!
Or just simply:
complete: alert("xpto");

Related

Pass function reference to a separate Javascript file for callback

I have created a common Javascript file for all my ajax calls. I am trying to use this as a common way to keep track of all ajax calls. Below is the code for the same.
function doAjax(doAjax_params) {
var url = doAjax_params['url'];
var requestType = doAjax_params['requestType'];
var contentType = doAjax_params['contentType'];
var dataType = doAjax_params['dataType'];
var data = doAjax_params['data'];
var beforeSendCallbackFunction = doAjax_params['beforeSendCallbackFunction'];
var successCallbackFunction = doAjax_params['successCallbackFunction'];
var completeCallbackFunction = doAjax_params['completeCallbackFunction'];
var errorCallBackFunction = doAjax_params['errorCallBackFunction'];
//Make the ajax call
$.ajax({
url: getBaseURL() + url,
crossDomain: true,
type: requestType,
contentType: contentType,
dataType: dataType,
data: data,
success: function (data, textStatus, jqXHR) {
console.log(typeof successCallbackFunction);
debugger
//if (typeof successCallbackFunction === "function") {
successCallbackFunction(data);
//}
},
error: function (jqXHR, textStatus, errorThrown) {
if (typeof errorCallBackFunction === "function") {
errorCallBackFunction(errorThrown);
}
}
});
}
This code takes a list of parameters and creates an ajax request based on the parameteres. This code is saved in a file APIHandler.js.
I am trying to call this function from multiple files. An example call is below.
function RedirectToDashboard() {
var params = $.extend({}, doAjax_params_default);
params['url'] = `profile/5`;
params['successCallbackFunction'] = `testsuccess`
doAjax(params);
}
function testsuccess() {
alert("success");
}
When I run this function, I am able to make the call successfully. The only issue comes with the reference to callback function. console.log(typeof successCallbackFunction); returns string instead of function.
I thought maybe order of JS made a difference. I am loading APIHandler.js and then the page specific js. And this ajax call happens at button click, so both JS files are loaded before the ajax call is made.
Other than that, I think maybe I am sending the parameters wrong. That might be causing JS to consider function name as string. But I checked most of the google suggestions on how to pass function, and it seems it needs just the name.
Is there anything else that I might be missing here?
Damn it. I just figured out why it was causing the error. I used quotes when assigning the callback function. Right after posting the question, I realised what was wrong.
params['successCallbackFunction'] = 'testsuccess'
is supposed to be changed to
params['successCallbackFunction'] = testsuccess

I can't seem to break out of A $.each() loop

I can't seem to manage to break out of my each loop if the ajax returns an error. I've tried
return false;
and other similar thing but the $.each still continues to run.
I need to be able to do this so that I can display error messages from my back end after posting it via ajax(I know this is bad practice however a client needed to be able to be able to send multiple forms off at once.).
Can anyone explain what I've done wrong?
var postAll = function(button_clicked)
{
e.preventDefault();
var form_response = [];
var formsCollection = document.getElementsByTagName("form");
$.each(formsCollection, function (key, value)
{
console.log(value.action);
console.log(value.id);
var url = value.action;
var id = value.id;
var data = ($('#' + id + '').serialize());
if (id == 'additionalInfo')
{
data = {'Add_info': $('#Add_info').val(),};
}
if (id != 'DONE')
{
$.ajax({
type: "POST",
dataType: 'json',
url: url,
beforeSend: function (xhr)
{
xhr.setRequestHeader('X-CSRF-TOKEN',$("#token").attr('content'));
},
data: data,
success: function (data)
{
console.log('success'); // show response from the php script.
form_response.push(data); // show response from the php script.
},
error: function (data)
{
console.log('fail'); // show response from the php script.
display_errors(data, id); // show response from the php script.
return true;
}
});
}
});
}
AJAX is asynchronous, when executing your $.each function it will execute the AJAX call and "Not wait" for the others to finish
To solve your problem you'll have to write a function that will execute the first ajax call and in the success it will execute itself again with the second ajax call.
Example:
var data = [form1,form2...etc];
function letsLoop(data,index = 0){
$.ajax({
url:....
success: function(){
letsLoop(data,index+1);
},
error: function(){
}
});
}
and here you call your function:
letsLoop(data,0);
If by breaking out of the loop you mean the return in your error handler, then it won't work as you think it would.
Your loop creates asynchronous requests 'at once'. Then each of these requests is handled by the browser (more or less simultaneously), then come responses. So by the time your error handler runs the loop has long finished.
BTW, the return in your case relates to the error handler, not the function inside the loop.
So, to achieve what you want you should 'queue' your AJAX requests and perform them one by one.
One possible solution is to create an array of forms then take (and remove it from the array) the first one, perform a request, on a response repeat the whole thing, and keep repeating until the array is empty.

Use data from two separate JSON files in one function

I have two local JSON files that I'm trying to access in a function in my main.js file. Everything works fine with just one JSON file, but I'm not sure how to incorporate the second. Ideally, something like data_set1=$.getJSON("file1.json") would work perfectly, but I see similar questions have been repeatedly asked and because of asynchronous calls, that's not necessarily possible (I don't completely understand all the answers to those questions).
This works as it is:
$.getJSON("data.json", function(json){
var data_points = [];
for (i = 0; i < json.length; i++){
data_points.push([json[i].name, json[i].age]);
}
$(function () {
//do stuff with data_points
but I don't know how to incorporate the second JSON call to make another list for the function at the end to use.
You can use jQuery deferred loading.
var xFile, yFile;
var requestX = $.getJSON("data1.json", function(json){
xFile = json;
});
var requestY = $.getJSON("data2.json", function(json){
yFile = json;
});
$.when(requestX, requestY).then(function(){
// do something;
// this function only gets called when both requestX & requestY complete.
});
Check out JQuery WHEN
An ajax request is ONE request for ONE resource. You can't fetch two different resources with the same request. You'd either need TWO requests:
var completed = 0;
$.getJSON('file1.json', function(json) {
completed++;
if (completed == 2) { all_done(); }
}
$.getJSON('file2.json', function(json) {
completed++;
if (completed == 2) { all_done(); }
}
function all_done(...) { ... }
Or simply combine your two json files into a single one on the server:
{"file1":{data from file one here}, "file2":{data from file two here}}
and access them as data.file1 and data.file2 in your code.
You could just set a var to indicate the file is loaded and call the same function while checking the status. This might be a bad idea depending on the amount of data in the JSON.
var xFileLoaded = false, yFileLoaded = false;
var xFile, yFile;
$.getJSON("data.json", function(json){
xFile = json;
xFileLoaded = true;
doSomething();
});
$.getJSON("data.json", function(json){
yFile = json;
yFileLoaded = true;
doSomething();
});
function doSomething() {
if(xFileLoaded && yFileLoaded) {
// good to go
}
}
Using something very similar to #MarcB's solution, I would keep the files in an array and generalize things slightly:
var files = ["file1.json", "file2.json"];
var results = {};
var completed = 0;
function afterEach(fileName, json) {
results[fileName] = json;
if (++completed >= files.length) {
afterAll();
}
}
files.forEach(function (file) {
$.getJSON(file, afterEach.bind(this, file));
});
This should request all files in parallel, allowing the browser to choose how many connections to actually open and download. After each finishes, the results (json) are put in a hash for storage, under the filename. After all files have completed (assuming files doesn't change), then afterAll function will be called.
It exists with jquery an elegant way to do this :
$.when(
$.ajax({
url: 'file1.json/',
success: function(data) {
// Treatement
}
}),
$.ajax({
url: 'file2.json',
success: function(data) {
// Treatement
}
})
).then( function(){
// Two calls are completed
});
Once the two calls are completed, you can do any manipulation in the block "then".

multiple calls to ajax simultaneously

I'm trying to make multiple calls to Ajax, i have fields like time intervals and no of calls to ajax under that time period. Now the problem is, while making multiple calls to same Ajax, there may be chances of merging of data with the other data that were send to Ajax earlier. I am not sure that it will happen.
Here my Ajax call.
callAjax = function () {
var dataIn = inObj.data || {};
var successFunc = inObj.success || function () {};
var passOn = inObj.passOn || {};
var myParams = {drape:1,type:'GET'};
myParams.url = this.homeComingUrl;
$.extend(myParams,params);
var data = this.fillAction(action,dataIn);
if (myParams.drape) { vidteq.utils.drapeSheer(action); }
var that = this;
var magicCall = $.ajax({
url:myParams.url,
type:myParams.type,
data:data,
success: function (response) {
// TBD we need better error handling
if (myParams.drape) { vidteq.utils.undrapeCurtain(action); }
successFunc(response,passOn);
},
error:function(response) {
if (myParams.drape) { vidteq.utils.undrapeCurtain(action); }
that.gui.io.handleError(response);
}
});
}
saveEvents = function () {
this.commitEditingEvent();
var dataEvents = this.collectEventsToSave();
//$('#calendar').fullCalendar('removeEvents');
var that = this;
if (vidteq.eTrainer==1) {
dataEvents = arguments[0];
}
if (!dataEvents.length) { alert("Nothing to save");return; }
this.callAjax('updateEvents',{
data : { events : JSON.stringify(dataEvents) },
success : function (response,passOn) {
that.handleGetEvent(response,passOn);
}
},{type:'POST'});
}
This may not be required for understanding the problem.
If any body can explain how Ajax handles multiple calls, then it'll really helpful.
First line, your anonymous function isn't saved and isn't ran. Then. In each function, what does this refer to ? What is this context ? Is this window or do you call your function like saveEvents.apply( jQuery ) ?
JavaScript is powerful, when your want to run XMLHttpRequest (Ajax uses it), scripts are called when an event happen, like "server is found", "request is send", "file is reading", "file loaded"... for each state of your request. Ajax by jQuery help you to request asynchronous. You can request as many Ajax request as you would like in the same time. The important is to create a function happen in success case.
In this success function, you receive data, you compute it, then this function may call another Ajax request, and so on. When you chain requests like this to get the same file, we call it Ressource.
Ressource uses Ajax which uses XMLHttpRequest.
you need to do asynic :false in your ajax method
function isLoggedIn() {
var isLoggedIn;
$.ajax({
async: false,
// ...
success: function(jsonData) {
isLoggedIn = jsonData.LoggedIn
}
});
return isLoggedIn
}

Making functions wait until AJAX call is complete with jQuery

Im trying to develop a class in JavaScript I can use to access a load of data that is gathered by an AJAX request easily. The only problem is I need to make the members of the class accessible only once the AJAX call is complete. Ideally what I would like to end up is something where by I can call this in a script:
courses.getCourse('xyz').complete = function () {
// do something with the code
}
And this will only fire after the AJAX call has been complete and the data structures in the "class" are ready to be used. Ideally I dont want to have to create a .complete member for every function in the class
Here is the "class" I am trying to make so far:
var model_courses = (function() {
var cls = function () {
var _storage = {}; // Used for storing course related info
_storage.courses = {}; // Used for accessing courses directly
_storage.references = new Array(); // Stores all available course IDs
var _ready = 0;
$.ajax({
type: "GET",
url: "data/courses.xml",
dataType: "xml",
success: function(xml) {
$(xml).find("course").each(function() {
_storage.courses[$(this).attr('id')] = {
title : $(this).find('title').text(),
description : $(this).find('description').text(),
points : $(this).find('points').text()
}
_storage.references.push($(this).attr('id'))
})
}
})
console.log(_storage.courses)
}
cls.prototype = {
getCourse: function (courseID) {
console.log(cls._storage)
},
getCourses: function () {
return _storage.courses
},
getReferences: function (),
return _storage.references
}
}
return cls
})()
At the moment getCourse will be fired before the AJAX request is complete and obviously it will have no data to access.
Any ideas will be greatly appreciated, im stuck on this one!
jQuery already handles this for you using deferred objects, unless i'm misunderstanding what you are looking for.
var courses = {
getCourse: function (id) {
return $.ajax({url:"getCourse.php",data:{id:id});
}
};
courses.getCourse("history").done(function(data){
console.log(data);
});
I know this isn't exactly what you are looking for, I'm hoping it's enough to push you in the right direction. Deferred objects are awesome.
The following changes allow you to make the AJAX request just once and you can call your function like
courses.getCourse('xyz', function(course){
// Use course here
});
Here are the changes
var model_courses = (function() {
// This is what gets returned by the $.ajax call
var xhr;
var _storage = {}; // Used for storing course related info
_storage.courses = {}; // Used for accessing courses directly
_storage.references = []; // Stores all available course IDs
var cls = function () {
xhr = $.ajax({
type: "GET",
url: "data/courses.xml",
dataType: "xml",
success: function(xml) {
$(xml).find("course").each(function() {
_storage.courses[$(this).attr('id')] = {
title : $(this).find('title').text(),
description : $(this).find('description').text(),
points : $(this).find('points').text()
}
_storage.references.push($(this).attr('id'))
});
}
});
}
cls.prototype = {
// Made changes here, you'd have to make the same
// changes to getCourses and getReferences
getCourse: function (courseID, callback) {
if (xhr.readyState == 4) {
callback(_storage.courses[courseID]);
}
else {
xhr.done(function(){
callback(_storage.courses[courseID]);
})
}
},
getCourses: function () {
return _storage.courses
},
getReferences: function (),
return _storage.references
}
}
return cls
})()
As a side note, your module pattern will not work very well if you need to instantiate two of these model_courses objects, since the storage objects are all shared in your self calling function's closure. You usually don't mix the module pattern with prototypes (returning a constructor from a module), unless you really know what you are doing, that is, the shared closure variables work as static properties of your class.
This is what I would do if I were you (since you really want private variables)
function ModelCourses() {
var storage = {
courses: {},
references: []
};
var xhr = $.ajax({
type: "GET",
url: "data/courses.xml",
dataType: "xml",
success: function(xml) {
$(xml).find("course").each(function() {
storage.courses[$(this).attr('id')] = {
title : $(this).find('title').text(),
description : $(this).find('description').text(),
points : $(this).find('points').text()
}
storage.references.push($(this).attr('id'))
})
}
});
this.getCourse = function(courseId, callback) {
function getCourse() {
callback(storage.courses[courseID])
}
if (xhr.readyState == 4) {
getCourse();
}
else {
xhr.done(getCourse);
}
};
}
in getStorage either add a check to see if there is any data to pilfer (preferred), or make the "actual" method private than publicize it when it has items it can access. (I would recommend the first though otherwise you'll get exceptions about calling a method that doesn't exists on an object).
You can define a function getData that would perform the ajax request and that would take the getCourse as a callback.
The getData could possibly store locally the result of the Ajax call and test the local storage before performing the ajax call.
You could also specify a private member to allow the ajax call to be run only once.
You might want to check underscore.js for some handy tool
Here is a short example code :
cls.prototype.getData = function(callback) {
/*perform ajax call or retrieve data from cache*/
callback()
}
cls.prototype.getCourse = function(id) {
this.getData(function() {
/*do something with the data and the id you passed*/
}
}

Categories

Resources