I am well aware it can be done and I\'ve looked at quite a few places (including: Best practice for saving an entire collection?). But I\'m still not clear \"exactly how\"
Here's a simple example:
var Books = Backbone.Collection.extend({
model: Book,
url: function() {
return '/books/';
},
save: function(){
Backbone.sync('create', this, {
success: function() {
console.log('Saved!');
}
});
}
});
When you call the save() method on your collection, it will send a PUT method request to the defined URL.
A very simple...
Backbone.Collection.prototype.save = function (options) {
Backbone.sync("create", this, options);
};
...will give your collections a save method. Bear in mind this will always post all the collection's models to the server regardless of what has changed. options are just normal jQuery ajax options.
My immediate thought is not to override the method on save method on Backbone.Collection but wrap the collection in another Backbone.Model and override the toJSON method on that. Then Backbone.js will treat the model as a single resource and you don't have to hack the way backone thinks too much.
Note that Backbone.Collection has a toJSON method so most of your work is done for you. You just have to proxy the toJSON method of your wrapper Backbone.Model to the Backbone.collection.
var MyCollectionWrapper = Backbone.Model.extend({
url: "/bulkupload",
//something to save?
toJSON: function() {
return this.model.toJSON(); // where model is the collection class YOU defined above
}
});
The answer depends on what you want to do with the collection on server side.
If you have to send additional data with the post you might need a wrapper model or a relational model.
With the wrapper model you always have to write your own parse method:
var Occupants = Backbone.Collection.extend({
model: Person
});
var House = Backbone.Model.extend({
url: function (){
return "/house/"+this.id;
},
parse: function(response){
response.occupants = new Occupants(response.occupants)
return response;
}
});
Relational models are better I think, because you can configure them easier and you can regulate with the includeInJSON option which attributes to put into the json you send to your rest service.
var House = Backbone.RelationalModel.extend({
url: function (){
return "/house/"+this.id;
},
relations: [
{
type: Backbone.HasMany,
key: 'occupants',
relatedModel: Person,
includeInJSON: ["id"],
reverseRelation: {
key: 'livesIn'
}
}
]
});
If you don't send additional data, you can sync the collection itself. You have to add a save method to your collection (or the collection prototype) in that case:
var Occupants = Backbone.Collection.extend({
url: "/concrete-house/occupants",
model: Person,
save: function (options) {
this.sync("update", this, options);
}
});
Old thread i know, what i ended up doing is the following:
Backbone.Collection.prototype.save = function (options) {
// create a tmp collection, with the changed models, and the url
var tmpCollection = new Backbone.Collection( this.changed() );
tmpCollection.url = this.url;
// sync
Backbone.sync("create", tmpCollection, options);
};
Backbone.Collection.prototype.changed = function (options) {
// return only the changed models.
return this.models.filter( function(m){
return m.hasChanged()
});
};
// and sync the diffs.
self.userCollection.save();
Pretty straint foreward :)
The accepted answer is pretty good, but I can go one step further and give you code that will ensure the proper events are fired for your listeners while also allowing you to pass in option ajax event callbacks:
save: function( options ) {
var self = this;
var success = options.success;
var error = options.error;
var complete = options.complete;
options.success = function( response, status, xhr ) {
self.trigger('sync', self, response, options);
if (success) return success.apply(this, arguments);
};
options.error = function( response, status, xhr ) {
self.trigger('error', self, response, options);
if (error) return error.apply(this, arguments);
};
options.complete = function( response, status, xhr ) {
if (complete) return complete.apply(this, arguments);
}
Backbone.sync('create', this, options);
}