How to override Backbone.sync?
Take a look at this annotated source example where they overwrite Backbone.sync
with a localstorage alternative
backbone-localStorage
Basically Backbone.sync should be a function that takes 4 arguments:
Backbone.sync = function(method, model, options) { };
You need to fire either options.success
or options.error
depending on whether the method
succeeded. The methods are in the format:
"create"
: expected that you create the model on the server"read"
: expected that you read this model from the server and return it"update"
: expected that you update the model on the server with the argument"delete"
: expected that you delete the model from the server.
You need to implement those 4 methods and define whatever you want for your "server"
Of course these are only the things that Backbone.sync
must implement. You may implement more methods
and you may pass more paramaters back to success
but it's best not to do this.
It's best to make sure it does the same as Backbone.sync
does currently so that your programming to an interface rather then an implementation. If you want to switch out your modified Backbone.sync
for say the localStorage one you won't have to extend it yourself to match your extended Backbone.sync"
[Edit]
Also do note that you can use multiple implementations of sync
. Every reference to Backbone.sync
is actaully (this.sync || Backbone.sync)
so you just have to do something like:
var MyModel = Backbone.Model.extend({
...
"sync": myOwnSpecificSync,
...
});
Backbone.sync
is just the default global one that all models use unless the models have a sync
method specifically set.
Override Backbone.sync() at Model level to send extra params?
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 thecontentType
when you've specified data to be sent with the delete request.- Pros: You can call
model.destroy()
and optionally pass in anoptions
parameter
- Pros: You can call
- 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
andcontentType
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
How do you override the sync method for a Backbone collection (as opposed to a model)?
Backbone's Collection sync looks exactly the same as the Model's sync method:
// Proxy `Backbone.sync` by default.
sync: function() {
return Backbone.sync.apply(this, arguments);
},
thats because both do the same, they simply "proxy" the Backbone.sync method. The reason they are there is to allow implementations to change the sync logic on a per type basis and not having to touch the main sync method which will influence all models and collections in your project.
I would advise doing something like the following for your collection because you probably dont want to mimic Backbone's sync logic yourself, it does quite a few things for you and messing with it can cause problems that could be hard to solve later on.
var MyCollectionType = Backbone.Collection.extend({
sync: function(method, model, options){
//put your pre-sync logic here and call return; if you want to abort
Backbone.Collection.prototype.sync.apply(this, arguments); //continue using backbone's collection sync
}
});
Overriding Backbone Sync to use diiferent calls for fetch/save/destroy
You can provide your collections or models with a custom sync
function which will be called instead of Backbone.sync
when you fetch/update/destroy an element. You can then tailor the options to emit a request matching your server setup. For example,
var M = Backbone.Model.extend({
sync: function(method, model, options) {
options || (options = {});
// passing options.url will override
// the default construction of the url in Backbone.sync
switch (method) {
case "read":
options.url = "/myservice/getUser.aspx?id="+model.get("id");
break;
case "delete":
options.url = "/myservice/deleteUser.aspx?id="+model.get("id");
break;
case "update":
options.url = "/myService/setUser.aspx";
break;
}
if (options.url)
return Backbone.sync(method, model, options);
}
});
var c = new M({id: 1});
c.fetch();
c.save();
c.destroy();
And a Fiddle simulating these calls http://jsfiddle.net/nikoshr/4ArmM/
If using PUT and DELETE as HTTP verbs bothers you, you can force a POST by adding Backbone.emulateHTTP = true;
See http://jsfiddle.net/nikoshr/4ArmM/1/ for a revised version.
override backbone.sync only for put
Backbone._sync = Backbone.sync;
Backbone.sync = function(method, model, options) {
var params = _.clone(options);
delete model.attributes.id;
params.success = function(model) {
if(options.success) options.success(model);
};
params.error = function(model) {
if(options.error) options.error(model);
};
Backbone._sync(method, model, params);
}
Where to code inorder to override backbone.sync
The strategy behind Backbone framework is to make it simple for editing and flexible for every need. So if you look up the source code you'll find out that every method, which calls Backbone.sync in fact calls first "this.sync".
From the Backbone manual you can read :
The sync function may be overriden globally as Backbone.sync, or at a
finer-grained level, by adding a sync function to a Backbone
collection or to an individual model.
So you have two options
Option One - Replacing global Backbone.sync function
If you override the global Backbone.sync you should place your code in your global application file ( actually anywhere you want, but it must be evaluated ( executed ) at your initial javascript loading, to work as expected
// Anywhere you want
Backbone.sync = function(method, collection, options) {
console.log(method, collection options)
}
This will override Backbone.sync and actually will display on your console what is called every time you call collection.fetch, save, delete, etc.
Here you have no default Methodmap, infact you have nothing else except the arguments :
- method - which is a string - 'read', 'create', 'delete', 'update'
- collection - which is your collection instance which calls the method
- options - which has some success, error functions, which you may or may not preserve.
Debug this in your browser, while reading the Backbone source code, it's very easy to understand.
Option Two - Adding to your model/collection sync method
This is used if you wish to use the default Backbone.sync method for every other model/collection, except the one you specifically define :
mySocketModel = Backbone.Model.extend({
sync : function(method, collection, options) {
console.log('socket collection '+this.name+' sync called');
}
});
Partners = new mySocketModel({ name : 'partners' });
Users = new mySocketModel({ name : 'users' });
Log = new Backbone.Collection;
So if you call Partners.fetch() or Users.fetch(), they won't call Backbone.sync anymore, but yor Log.fetch() method will.
Backbone - overriding sync using Prototype not assigning values
Instead of sync()
try sync.call(this, arguments)
Overriding Backbone sync with a progress event listener
Here's what finally worked for us:
sync: function(method, model, options) {
options.beforeSend = function(xhr, settings) {
settings.xhr = function() {
var xhr = $.ajaxSettings.xhr();
xhr.upload.addEventListener("progress", function (event) {
Math.ceil(event.loaded/event.total*100);
}, false);
return xhr;
}
}
return Backbone.sync(method, model, options);
}
Related Topics
Keyword 'Const' Does Not Make the Value Immutable. What Does It Mean
Creating an Iframe with Given HTML Dynamically
How to Match Multiple Occurrences with a Regex in JavaScript Similar to PHP's Preg_Match_All()
Why Does Settimeout() "Break" for Large Millisecond Delay Values
How to Convert an Iso Date to the Date Format Yyyy-Mm-Dd
How to Apply CSS to the Elements Within an Iframe
Location.Host VS Location.Hostname and Cross-Browser Compatibility
Scale a Div to Fit in Window But Preserve Aspect Ratio
Img-Src' Was Not Explicitly Set, So 'Default-Src' Is Used as a Fallback
Keys in JavaScript Objects Can Only Be Strings
Failed to Load C++ Bson Extension
Correct Use of Arrow Functions in React
JavaScript Sort Function. Sort by First Then by Second
Differencebetween the Different Scroll Options
Set Http Header for One Request
Uncaught Referenceerror: $ Is Not Defined Error in Jquery
Storing Image Data for Offline Web Application (Client-Side Storage Database)