Monday, June 10, 2013

Regarding callbacks when saving a Backbone model

This one stumped me for a while. Backbone allows you to specify success and error callbacks when saving a model. But if you go by the documentation, this is how you create and save a new model:

submitForm: function(e){
 e.preventDefault();
 
 var pub = new Publication({
  'title': this.$('input[name="title"]').val()
 });
 pub.save({
  success: this.onSuccess
 }) 
},

onSuccess: function(model, response, options){
 Vent.trigger('imported:publication');
 this.$('form').trigger('reset');
},

But with the above, while the model will be correctly saved, the success callback will not be called. Reason being, the first argument to the save call is mandatory unlike what the documentation says. So something like the following will work:

 pub.save({}, {
  success: this.onSuccess
 }) 

Notice the empty object being passed as the first argument. You can also pass null if you like that but I still get nightmares of NullPointerException from my Java days. So I try and steer clear of it.

The next problem you may run into is that `this` does not seem to be set to correct value in the callback. In general, any callbacks being called from non-backbone or marionette code will suffer from this problem. This can be handled by a call to bindAll while initializing the view:

initialize: function(){
 _.bindAll(this);
},

Another thing you will sooner or later start missing here is the lack of a 'onComplete' callback that gets called irrespective of success and failure. This callback is useful for things like enabling the submit button that you may have disabled on submission or removing the waiting icon.

Fortunately, the save method returns a jQuery deferred object. So we are free to set our own onComplete callbacks on it:

formSubmit: function(e){
 e.preventDefault();
 
 this.$('button').hide();
 
 var pub = new Publication({
  'title': this.$('input[name="title"]').val()
 });
 pub.save({}, {
  success: this.onSuccess
 })
 .complete(this.onComplete);
 
},

onSuccess: function(model, response, options){
 Vent.trigger('imported:publication');
 this.$('form').trigger('reset');
},

onComplete: function(){
 // Display the submit button again
 this.$('button').show();
}

In fact we can do the same for the success and error callbacks as well.

pub.save().success(this.onSuccess).complete(this.onComplete);


While passing a success callback or using the deferred to setup a success callback are identical, in case of error callback, if you do not specify one in the arguments, an error event is triggered.

No comments:

Post a Comment