When you invoke .destroy(), .fetch() or .save(), they all trigger Model.sync which ultimately invokes Backbone.sync. Acting as a proxy function, this allows for easy manipulation of the AJAX behavior of a single model or any models that inherit from it.
- Solution 1: Replace the Global Backbone.sync with
JSON.stringify
and adjust the contentType
when sending data with the delete request.
- Pros: Ability to call
model.destroy()
and pass an optional options
parameter.
- Solution 2: Override the Model.sync method directly.
- Pros: The override is specific to individual models, leading to isolated changes.
- Cons: Models requiring data deletion need to extend from the correct 'base model'.
- Solution 3: Refrain from overriding and instead manually invoke model.sync with appropriate
stringify
and contentType
.
- Pros: Changes are contained within the specific model, without impacting others. Particularly useful in large codebases integration scenarios.
Javascript version:
var oldBackboneSync = Backbone.sync;
Backbone.sync = function( method, model, options ) {
// Ensure data is formatted properly for DELETE requests
if ( method === 'delete' && options.data ) {
options.data = JSON.stringify(options.data);
options.contentType = 'application/json';
} // Otherwise follows normal flow
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 */
}
});
Override Example:
// Create custom 'enhanced' model
Backbone.EnhancedModel = Backbone.Model.extend({
destroy: function( options ) {
if ( options.data ) {
// Format data appropriately for backend parsing
options.data = JSON.stringify(options.data);
}
// Set content type for 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 */
}
});
If handling data in destroy requests separately, this approach works effectively.
When utilizing model.destroy()
, include data
and contentType
options like so:
Javascript version/usage:
var additionalData = { collective_id: 14 };
model.destroy({
data: JSON.stringify(additionalData),
contentType: 'application/json'
});
The "Issue" (regarding Backbone, not solutions):
Backbone.js assumes (view source) that delete requests should not contain a data payload.
// Data processing and content type alteration excluded for delete methods
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 its expected RESTful API call, only the ID needs to be included in the request, appended via the urlRoot
property.
var BookModel = Backbone.Model.extend({
urlRoot: 'api/book'
});
var book1 = new BookModel({ id: 1 });
book1.destroy()
The resulting DELETE request would be:
DELETE => api/book/1
contentType: Content-Type:application/x-www-form-urlencoded; charset=UTF-8