Sync an entire collection of models in backbone.js

10,356

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.

Share:
10,356
Thomas
Author by

Thomas

Just trying to learn things...

Updated on June 19, 2022

Comments

  • Thomas
    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
    Thomas over 11 years
    So, I tried sync but I am getting the same error. syncCollection: function() { this.sync('create', this); } - Am I still screwing something up?
  • machineghost
    machineghost over 11 years
    If 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
    machineghost over 11 years
    Also 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's initialize function.
  • Thomas
    Thomas over 11 years
    Yes, 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
    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
    Thomas over 11 years
    We 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
    machineghost over 11 years
    Could you please post the code where you instantiate RegisterCollection (ie. the code that does new RegisterCollection())?
  • machineghost
    machineghost over 11 years
    Hmmmm ... I think you need to do some debugging. Can you add the line console.log(this.collection) just before the this.collection.syncCollection();? Clearly this.collection isn't what we think it is, but I'm curious what it actually is.
  • machineghost
    machineghost over 11 years
    P.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
    Thomas over 11 years
    Here 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
    Txangel almost 10 years
    I think that you are reinventing the wheel here.
  • machineghost
    machineghost almost 10 years
    Ah, nice find. Does using sync that way instead make a single AJAX request (vs. my solution which would make one request per model)?
  • Thomas
    Thomas almost 10 years
    If 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
    reggie almost 9 years
    Angular !== 'sane'. Backbone = ♥
  • Ryan
    Ryan almost 9 years
    Fight 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.