Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Override Backbone.sync() at Model level to send extra params?

Tags:

To be quite honest I'm stuck trying to override Backbone's sync() method for a Model, I have the signature for the function in place, and it gets triggered correctly, but I don't know what to put in the function body in order for it to make a default call to DELETE but with extra arguments. ie.

class Master.Models.Member extends Backbone.Model   urlRoot: '/api/members/'    sync: (method, model, options) ->     params = _.clone options     Backbone.sync method, model, params 

I call it like this:

...... remove: ->   @model.destroy     collective_id: the_id 

My intention there, is to pass the collective_id param you see there to the server. But even though it's inside the options hash for sync() and I clone it, It won't make it to the server! How can I send that extra param to the server?

(As it is, the only thing that reaches the server is the Model's id)

Thanks in advance!

like image 486
jlstr Avatar asked May 09 '13 23:05

jlstr


2 Answers

When you call .destroy(), .fetch() or .save() they all call Model.sync which only calls Backbone.sync. It's a proxy function. This is intended to provide a nice hook for modifying the AJAX behavior of a single model or any models that extend from that model.

  • Solution 1: Override the Global Backbone.sync to JSON.stringify and modify the contentType when you've specified data to be sent with the delete request.
    • Pros: You can call model.destroy() and optionally pass in an options parameter
  • Solution 2 - Override the Model.sync method.
    • Pros: The override only affects individual models. Isolated changes.
    • Cons: All models that wish to delete with data need to extend from the correct 'base model'
  • Solution 3 - Don't override anything and explicitly call model.sync with all of the stringify and contentType stuff.
    • Pros: Very isolated changes, won't affect any other models. Useful if you're integrating with a large codebase.

[Solution 1] - Global Override of Backbone.sync (this will affect all models)

javacript version

var oldBackboneSync = Backbone.sync; Backbone.sync = function( method, model, options ) {     // delete request WITH data     if ( method === 'delete' && options.data ) {         options.data = JSON.stringify(options.data);         options.contentType = 'application/json';     } // else, business as usual.     return oldBackboneSync.apply(this, [method, model, options]); } 

Usage:

var model, SomeModel = Backbone.Model.extend({ /* urlRoot, initialize, etc... */}); model = new SomeModel(); model.destroy({     data: {         /* data payload to send with delete request */     } }); 

[Solution 2] - Override Backbone.destroy on a base model and extend other models from that.

override

// Create your own 'enhanced' model  Backbone.EnhancedModel = Backbone.Model.extend({     destroy: function( options ) {         if ( options.data ) {             // properly formats data for back-end to parse             options.data = JSON.stringify(options.data);         }         // transform all delete requests to application/json         options.contentType = 'application/json';         Backbone.Model.prototype.destroy.call(this, options);     } }); 

usage

var model, SomeModel = Backbone.EnhancedModel.extend({ /* urlRoot, initialize, etc... */}) model = new SomeModel(); SomeModel.destroy({     data: {         /* additional data payload */     } });  

[Solution 3] - Call .destroy() with correct parameters.

If sending data with your destroy requests is an isolated thing, then this is an adequate solution.

When calling model.destroy() pass in the data and contentType options like so:

javascript version/usage

var additionalData = { collective_id: 14 }; model.destroy({     data: JSON.stringify(additionalData),     contentType: 'application/json' }); 

The "Problem" (with Backbone, not solutions):

Backbone.js makes the assumption (view source) that delete requests do not have a data payload.

// delete methods are excluded from having their data processed and contentType altered. if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {       params.contentType = 'application/json';       params.data = JSON.stringify(options.attrs || model.toJSON(options)); } 

In their assumed RESTful API call, the only data required is the ID which should be appended to a urlRoot property.

var BookModel = Backbone.Model.extend({     urlRoot: 'api/book' }); var book1 = new BookModel({ id: 1 }); book1.destroy() 

The delete request would be sent like

DELETE => api/book/1 contentType: Content-Type:application/x-www-form-urlencoded; charset=UTF-8 
like image 99
15 revs Avatar answered Sep 30 '22 18:09

15 revs


Params need to be sent in options.data, so try:

coffeescript

remove: () ->   @model.destroy      data: JSON.stringify       collective_id: the_id,      contentType: 'application/json' 

javascript

remove: function() {   return this.model.destroy({     data: JSON.stringify({       collective_id: the_id     }),     contentType: 'application/json'   }); } 
like image 21
glortho Avatar answered Sep 30 '22 19:09

glortho