Passing local variables to AJAX callback functions in jQuery

This post solves two problems I had:

  • how to pass a local variable to an ajax callback
  • how to have the error handler to be called even on 404 or network errors

Example code below is also available on jsfiddle.net

What I want to implement is auto-updating checkboxes. I have some checkboxes, when I click on a checkbox, I want a loading icon to be shown, or like in this proof of concept, a “loading…” text, send an AJAX request, and when I receive the JSON response from the server I remove the loading icon, and show again the checkbox with the new status.

So far so good, I didn’t have any problem to implement the “happy path”, but the problems arise when the error callback is called. In this case I would like to show again the checkbox in the same status as it was before, and probably inform the user that the change couldn’t be saved.

[crayon language="javascript"]
$.ajax({
url: ‘/error-url’,
dataType: ‘json’,
success: successHandler,
error: failureHandler
});
[/crayon]

However, the parameters passed to the error callback are jqXHR, textStatus, errorThrown, but I would need to know which checkbox to show again. Furthermore, in case of network or 404 errors, the error callback is not called. I would call this a bug or weakness in jQuery (tested with 1.7.2).

Fortunately the complete callback is always called. The complete callback has parameters jqXhr, textStatus. However first we have to know whether it was called after a success or a failure.

I found out that a way to do it is checking jqXhr.status. This is the HTTP status, and it’s 0 in case of network problems, otherwise it can be 200 for successful request, 404 for page not found, or 5xx for server errors, and so on.

[crayon language="javascript"]
if (jqXhr.status == 0 || jqXhr.status >= 400) {
//it was an error!
}
[/crayon]

The other problem is to pass to the callback a variable to identify the checkbox, so I can show it again. The problem is that the callback is called by jQuery and we can’t add additional parameters to it. It is also called at a later time, when the AJAX request is completed. The trick is to build a closure, that we can use to store our local variables, and the closure contains a function factory. It may sounds very hard, but thanks to the genius JavaScript syntax is very easy.

[crayon language="javascript"]
$.ajax({

error: generateCheckIfError(fileId),
complete: generateCheckIfError(fileId)
});

function generateCheckIfError(fileId) {
return function (jqXhr, textStatus) {
if (jqXhr.status == 0 || jqXhr.status >= 400) {
$(‘#group-’ + fileId).html(‘done.’);
}
};
}
[/crayon]

With this syntax, when I define the callbacks the generateCheckIfError function executes, and traps the parameters inside the closure, then it returs a function pointer that will be bound to the callback. Inside that anonymous function I can still access fileId, hence my problem is solved.

Here is the whole proof of concept code, HTML:

[crayon language="html"]

Box 123
Box 156

[/crayon]

and JavaScript:

[crayon language="javascript"]
$(‘#check-group’).delegate(‘input’, ‘click’, checkboxClick);

function checkboxClick(event) {
$(event.target).hide().parent().html(‘loading…’);
var fileId = $(event.target).data(‘id’);
$.ajax({
url: ‘/error-url’,
dataType: ‘json’,
data: {
‘fileId’ : fileId,
‘newStatus’: event.target.checked
},
success: successHandler,
error: generateCheckIfError(fileId),
complete: generateCheckIfError(fileId)
});
}

function successHandler(data, textStatus, jqXhr) {
alert(‘success’);
}

function generateCheckIfError(fileId) {
return function (jqXhr, textStatus) {
if (jqXhr.status == 0 || jqXhr.status >= 400) {
$(‘#group-’ + fileId).html(‘done.’);
}
};
}
[/crayon]

In this case for simplicity I just write the word “done.“, when the request comes back with an error, instead of showing again the checkbox, but the whole point of this exercise is to show how to pass local variables from the AJAX functions to the callbacks.

Also for reference here are the parameters for success, complete and error callbacks:

success: data, textStatus, jqXHR
complete: jqXhr, textStatus
error: jqXHR, textStatus, errorThrown

Leave a Reply

  

  

  

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>