1

I'm having difficulty getting a model with an auto-incrementing "order" attribute working in BackboneJS.

For some reason every order gets set to 1. The length of the collection in the nextOrder function is always 0.

Options = _.extend(Options, {
    Models: {
        Profile: Backbone.Model.extend({
            defaults: function() {
                console.log("Defaults");
                return {
                    title: "New Profile",
                    order: Profiles.nextOrder(),
                    active: false
                };
            },
            url: "/youdontcare"
        })
});

Options = _.extend(Options, {
    Collections: {
        ProfileList: Backbone.Collection.extend({
            model: Options.Models.Profile,
            comparator: function(profile) {
                console.log("Comparator");
                return profile.get('order');
            },
            nextOrder: function() {
                console.log("nextOrder...");
                console.log(this.length);
                if (!this.length) return 1;
                return this.last().get('order') + 1;
            },
            url: "/youdontcare"
        })
});

Options = _.extend(Options, {
    Views: {
        ProfileView: Backbone.View.extend({
            tagName: "li",
            template: _.template($('#profile-template').html()),
            render: function() {
                console.log("Render Profile");
                this.$el.html(this.template(this.model.toJSON()));
                return this;
            }
        }),
        ProfileListView: Backbone.View.extend({
            el: $("#auth_env_list"),
            initialize: function() {
                Profiles = new Options.Collections.ProfileList;
                console.log("INIT LIST");

                this.listenTo(Profiles, 'add', this.addOne);
                this.listenTo(Profiles, 'reset', this.addAll);
                this.listenTo(Profiles, 'all', this.render);

                // Suppresses 'add' events with {reset: true} and prevents the app view 
                // from being re-rendered for every model. Only renders when the 'reset'
                // event is triggered at the end of the fetch.
                console.log("Fetching");
                Profiles.fetch({ reset: true });
                console.log("Done fetching");
            },
            addOne: function (profile) {
                console.log("addOne");
                var view = new Options.Views.ProfileView({ model: profile });
                this.$el.append(view.render().el);
            },
            addAll: function () {
                console.log("addAll");
                this.$el.html('');
                Profiles.each(this.addOne, this);
            },
            render: function() {
                console.log("RENDER PROFILE LIST VIEW");

                if (Profiles.length)
                {

                }
            }
        })
});

I can see that the nextOrder function inside the Profiles instance of the Options.Collections.ProfileList collection is called the appropriate number of times for each element that is fetched for the collection... however the length of the collection it tries to compute with this.length always returns 0!

Console output with 5 "Profile" elements:

INIT LIST
Fetching 
RENDER PROFILE LIST VIEW
Done fetching
Defaults
nextOrder... 
0
Defaults
nextOrder...
0
Defaults
nextOrder...
0
Defaults 
nextOrder...
0
Defaults
nextOrder...
0
Comparator 
addAll
addOne
Render Profile
addOne
Render Profile
addOne
Render Profile
addOne
Render Profile
addOne
Render Profile
RENDER PROFILE LIST VIEW
RENDER PROFILE LIST VIEW

Is there a better way I could assign an auto incrementing client side ID to these? The only reason I want to do it is to display them in a numbered list.

PrestaShopDeveloper
  • 3,110
  • 3
  • 21
  • 30
ashgromnies
  • 3,266
  • 4
  • 27
  • 43

1 Answers1

2

By calling the collection via the model, you're kind of creating a circular reference, and it's not very efficient if you were going to reuse the code on a different collection. It's possible that you're getting 0 back because it's not referring to the actual collection instance. A better way to accomplish what you want would be to have the collection assign the order number whenever a new model is added to the collection:

// 1st, get rid of the adding an order in your model
// in your collection, add something like the following
initialize: function() {
  this.on('add', this.addOrderID);
  this.on('reset', this.addOrderIDs);
},
addOrderID: function(model) {
  var order = this.length;
  model.set({'order': order});
},
addOrderIDs: function() {
  var order = this.length;
  this.models.forEach( function(model) {
    model.set({'order': order});
    order++;
  }, this);
}

I think that should accomplish what you're looking for.

EmptyArsenal
  • 7,314
  • 4
  • 33
  • 56
  • That didn't quite work -- the addOrderID function was never getting called on the collection. I wound up adding an 'order' integer property to the Profiles collection and then changing the nextOrder function to "return this.order++". – ashgromnies Oct 01 '13 at 17:35
  • The reason, I think, the code didn't work is that calling the initial fetch didn't trigger the 'add' event. If you add an event for 'reset', it should handle the initial population of the collection. However, I'm not 100% certain on all of this. – EmptyArsenal Oct 01 '13 at 17:45