I needed to catch a possible login page in all responses from the server, so I have overridden Backbone.sync globally so I can check all errors before passing them on.
Backbone.originalSync = Backbone.sync;
Backbone.sync = function (method, model, options) {
var originalSuccess, originalError;
console.log("sync override...");
// remember original success so we can call it if user logs in successfully
originalSuccess = options.success;
// proxy the error callback to first check if we get a login page back
originalError = options.error;
options.error = function (model, xhr, options) {
if (xhr.status === 200 && xhr.responseText === "") {
// parse error from empty response (jq1.9 invalid json, ok)
originalSuccess(model, xhr, options);
} else {
console.log("Sync error " + statusTxt + ", " + thrown.message);
if (xhr.status === 200 || xhr.status === 302 || xhr.status === 0) {
// login page returned instead of json...
// open a new window with relogon.html to trigger a new login
window.showModalDialog("../relogon.html");
} else {
// normal error, pass along
if (originalError) {
originalError(model, xhr, options);
}
}
}
};
// call the original sync
Backbone.originalSync(method, model, options);
};
This broke miserably when going from 0.9.9 to 1.0. Looks like the original Backbone.sync wraps its error handlers differently, causing my error handler to be called first, with a jquery xhr signature. I had to change the signature of the error handler to this:
options.error = function (xhr, statusTxt, thrown) {
Ok so now it works, but I get the feeling that I am doing something wrong.
Is there a better way to do this?
I tried with jquery promises but I need to be able to switch from error state to success (when calling originalSuccess), which did not seem to work with promises.
All sync errors are passed to model's error
event, so you may to listen to this event.
From http://backbonejs.org/#Events-catalog:
"error" (model, xhr, options) — when a model's save call fails on the server.
To capture error globally you may use http://api.jquery.com/ajaxError/
You can build your own jQuery Deferred object to alter the default Backbone.sync
behavior
Backbone.sync = function (method, model, opts) {
var xhr, dfd;
dfd = $.Deferred();
// opts.success and opts.error are resolved against the deferred object
// instead of the jqXHR object
if (opts)
dfd.then(opts.success, opts.error);
xhr = Backbone.originalSync(method, model, _.omit(opts, 'success', 'error'));
// success : forward to the deferred
xhr.done(dfd.resolve);
// failure : resolve or reject the deferred according to your cases
xhr.fail(function() {
if (xhr.status === 200 && xhr.responseText === "") {
dfd.resolve.apply(xhr, arguments);
} else {
if (xhr.status === 200 || xhr.status === 302 || xhr.status === 0) {
console.log('login');
}
dfd.reject.apply(xhr, arguments);
}
});
// return the promise to add callbacks if necessary
return dfd.promise();
};
The promise reflects the end state you choose.
http://jsfiddle.net/AsMYQ/4/ for a fail demo, http://jsfiddle.net/AsMYQ/5/ for a success.
And if I may
Backbone.sync
with your login handling. Use events, from Backbone or jQuery.ajaxError
as @Andrey suggestedIf you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With