Sync an entire collection of models in backbone.js
Solution 1
OK, a fresh pot of coffee and a day later, I got it. We were calling sync() improperly. Here is what we should have been doing:
syncCollection: function() {
Backbone.sync('create', this);
}
Thanks to machineghost for helping me explore this problem - I was totally focused on the wrong thing.
Solution 2
The reason you are getting that error is that Backbone.Collection
does not have a save method (its models do). What I think you're looking for is:
syncCollection: function() {
_(this.models).each(function(model) {
model.save();
});
}
Or better yet, using Collection
's each
:
syncCollection: function() {
this.each(function(model) {
model.save();
});
}
Or better still, using Collection
's invoke
:
syncCollection: function() {
this.invoke('save');
}
However, I'm not sure you even need any of that, as Backbone.Collection
already has a method that does (I think) what you want: sync
.
From the Backbone docs:
collection.sync(method, collection, [options])
Uses Backbone.sync to persist the state of a collection to the server. Can be overridden for custom behavior.
Comments
-
Thomas almost 2 years
I am attempting to sync an entire collection of models to the server in backbone.js. I want to POST a JSON package to the server containing all of the collection's model's attributes. This should be pretty simple but Im not sure what I am doing wrong:
var RegisterCollection = Backbone.Collection.extend({ initialize: function () {_(this).bindAll('syncCollection');}, model: RegisterModel, url: 'api/process.php', updateModel: function(registerModel) { var inputName = '#'+registerModel.get('inputName'); var userInput = $(inputName).val(); registerModel.set({value: userInput}); }, syncCollection: function(registerModel) { this.sync(); } }); var RegisterCollectionView = Backbone.View.extend({ el: "#register", events: { "click #submit" : "validate" }, validate: function(e) { e.preventDefault(); $("#register").valid(); if ($("#register").valid() === true) { this.updateModels(); this.collection.syncCollection(); } }, updateModels: function (){ this.collection.forEach(this.collection.updateModel, this);; }, initialize: function(){ this.collection.on('reset', this.addAll, this); }, addOne: function(registerModel){ var registerView = new RegisterView({model: registerModel}); this.$el.append(registerView.render().el); }, addAll: function(){ this.$el.empty(); this.collection.forEach(this.addOne, this); this.$el.append('<button type="submit" class="btn-primary btn" id="submit" style="display: block;">Submit</button>'); }, render: function(){ this.addAll(); return this; } }); var registerCollection = new RegisterCollection(); registerCollection.fetch(); var registerCollectionView = new RegisterCollectionView({collection: registerCollection}); registerCollectionView.render();
When I try to call syncCollection(), it returns the following error:
Uncaught TypeError: Object [object Object] has no method 'save'
I'm new to Backbone, so odds are that I am making a fairly elementary mistake somewhere... -
Thomas over 11 yearsSo, I tried sync but I am getting the same error.
syncCollection: function() { this.sync('create', this); }
- Am I still screwing something up? -
machineghost over 11 yearsIf you are getting the exact same "Uncaught TypeError: Object [object Object] has no method 'save'" error, then yes you are doing something very wrong, because your code should no longer even have a 'save' call ;-) But I doubt you're getting the exact same error, so could you please clarify exactly what error you got?
-
machineghost over 11 yearsAlso I can't help but wonder: how exactly are you invoking syncCollection ... it's not as a callback by any chance is it? When callbacks get invoked they often change the "this" of the callback, if it hasn't been previously bound. If you think this might be your problem, try calling
_(this).bindAll('syncCollection')
in your collection'sinitialize
function. -
Thomas over 11 yearsYes, I am using a call back so I tried your initialize binding which broke the script. I updated the original problem above so that you can see the full picture.
-
machineghost over 11 years
initialize:_(this).bindAll('syncCollection')
so what you just said there was "make my initialize function the result of a bindAll call", which is obviously not what you want :-) Try wrapping it in a function instead:initialize: function() {_(this).bindAll('syncCollection');},
. -
Thomas over 11 yearsWe must be on the same wavelength - I just realized that! Sadly, the error is still the same:
Uncaught TypeError: Object [object Object] has no method 'sync'
... -
machineghost over 11 yearsCould you please post the code where you instantiate
RegisterCollection
(ie. the code that doesnew RegisterCollection()
)? -
machineghost over 11 yearsHmmmm ... I think you need to do some debugging. Can you add the line
console.log(this.collection)
just before thethis.collection.syncCollection();
? Clearlythis.collection
isn't what we think it is, but I'm curious what it actually is. -
machineghost over 11 yearsP.S. For that to work you'll need a browser console, like the one from Firefox's Firebug extension (or Chrome's built-in dev tools).
-
Thomas over 11 yearsHere is what it spit out:
d {length: 5, models: Array[5], _byId: Object, _byCid: Object, syncCollection: function…
Looks like it is targeting the correct collection, since there are 5 models. -
Txangel almost 10 yearsI think that you are reinventing the wheel here.
-
machineghost almost 10 yearsAh, nice find. Does using sync that way instead make a single AJAX request (vs. my solution which would make one request per model)?
-
Thomas almost 10 yearsIf I recall, it did make a single request but Im not 100% sure. I started using Angular like a sane person and never looked back.
-
reggie almost 9 yearsAngular !== 'sane'. Backbone = ♥
-
Ryan almost 9 yearsFight fight fight fight! Nerd wars. Regardless of which is "better" they are both good for certain things, and sometimes you don't have a choice which to use. If your codebase is one you write it, not convert it.