Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

backbone.js collection adds empty element when created?

Tags:

backbone.js

I am assuming this is either a bug in my code or an undocumented (as far as I could find) feature of backbone.js. When I created my collection and my view there is already a model in that collection that I did not create, or I assume I did not create due to the undefined id. Below is my code.

// ---------------------------------------------------------- Work Order
window.WO = Backbone.Model.extend({
    default: {
        wonum: null,
        part:  null,
        desc:  null,
        comment: null,
        order: null,
        section: null
    },
    url: "/rest/wo/"
});
window.WOView = Backbone.View.extend({
    tagName: "tr",
    className: "wo",
    events: {
        "keypress .woComment"      : "updateOnEnter"
    },
    initialize: function(options)
    {
         _.bindAll(this, 'render', 'close', 'updateOnEnter');
        this.render = _.bind(this.render, this);
        this.model.bind('change', this.render);
    },
    render: function()
    {
        $(this.el).html(this.woTemplate(this.model.toJSON()));
        this.input = this.$('.woComment');
        this.input.bind('blur', this.close);
        return this;
    },
    woTemplate: _.template($('#woTemplate').html()),
    close: function()
    {
        this.model.set({comment: this.input.val()});
        this.model.save({},{contentType: 'application/jason'});
    },
    updateOnEnter: function(e) {
        if (e.keyCode == 13) this.close();
    }
});
// ------------------------------------------------------------- Section 
window.SectionC = Backbone.Collection.extend({
    comparator: function(woObj)
    {
        return woObj.get('order');
    }
});
window.Section = Backbone.Model.extend({
    defaults: {
        id: null,
        name: null
    },
    events: {
        'update' : 'doOrder',
        'change' : 'doOrder'
    },
    url: "/rest/section",
    woc: null,
    initialize: function()
    {
        this.woc = new SectionC({model: window.WO});
    },
    add: function(woObj)
    {
        this.woc.add(woObj);
        this.doOrder();
    },
    doOrder: function()
    {
        console.log("Calling doOrder");
        var that = this;
        var sel = "#sec"+this.get('id')+" .wo";
        $(sel).each(function(i,elem)
        {
            var elemID = $(elem).attr('id');
            var woObj = that.woc.get(elemID);
            woObj.set({order: i});
        });
    },
});

window.SectionView = Backbone.View.extend({
    tagName: "table",
    className: "section",
    initialize: function()
    {
        _(this).bindAll('add','remove','change');
        this.render = _.bind(this.render, this);
        this.mySort = _.bind(this.mySort, this);
    },
    sectionTemplate: _.template($('#sectionTemplate').html()),
    render: function()
    {
        this._rendered = true;
        var that = this;
        $(this.el).empty();
        $(this.el).attr('id',"sec"+this.model.get('id'));
        var woData = null;
        _(this.models).each(function(woObj)
        {
            var wov = new WOView({
                model: woObj,
                id: woObj.get('wonum')});
            woData += wov.render().el;
        });
        $(this.el).html(this.sectionTemplate({woData: woData}));
        return this;
    },
    add: function(woObj)
    {
        woObj.set({section: this.model.id, id: woObj.get('wonum')});
        this.model.add(woObj);
        if(this._rendered)
        {
            var wov = new WOView({
                model: woObj,
                id: woObj.get('wonum')});
            $(this.el).append(wov.render().el);
        }
        //this.mySort();
    },
    change: function()
    {
        this.render();
    },
    mySort: function()
    {
        var that = this;
        var sel = "#sec"+this.model.get('id')+" .wo";
        $(sel).each(function(i,elem)
        {
            var elemID = $(elem).attr('id');
            var woObj = that.model.woc.get(elemID);
            woObj.set({order: i});
        });
    },
    saveSection: function()
    {
        var json = {};
        json.section = this.model.get('id');
        json.order = {};
        var sel = "#sec"+this.model.get('id')+" .wo";
        $(sel).each(function(i,elem)
        {
            json.order[i] = $(elem).attr('id');
        });
        console.log(json);
        _(this.model.woc.models).each(function(woObj)
        {
            if(woObj.get('id') != "" && woObj.get('id') != undefined)
                woObj.save();
        });
    }
});
// ---------------------------------------------------------------- Page
window.PageC = Backbone.Collection.extend({
    comparator: function(obj)
    {
        return obj.get('order');
    }
});

window.PageView = Backbone.View.extend({
    tagName: "div",
    className: "prodSchedPage",
    initialize: function()
    {
        _(this).bindAll('add');
        this.render = _.bind(this.render, this);
    },
    render: function()
    {
        var that = this;
        this._rendered = true;
        $(this.el).empty();
        // Loop through the sections and render them
        _(this.collection.models).each(function(secObj)
        {
            var v = new SectionView({model: secObj, id: secObj.get('id')});
            $(that.el).append(v.render().el);
        });
        return this;
    },
    add: function(sectionObj)
    {
        this.collection.add(sectionObj);
        if(this._rendered)
        {
            this.render();
        }
    },
    addSection: function(sectionObj){this.add(sectionObj);},
    addWO: function(secID,woObj)
    {
        var secObj = this.collection.get(secID);
        if(secID = undefined)
        {
            alert("Error: Section does not exist!");
            return;
        }
        secObj.add(woObj);
    }
});

window.PSPage = new window.PageC({});
window.PSPV   = new window.PageView({collection: window.PSPage});
$("body").append(window.PSPV.render().el);
//window.PSPV.add(new Section({id: 1, name: "Section 1"}));
like image 736
Jason Avatar asked Jul 06 '11 16:07

Jason


3 Answers

When you instantiate the collection, the first argument is an array of models, the second argument is options.

window.PSPage = new window.PageC({});

When you pass in {} the constructor passes the arguments through the reset method to the add method and the the add method checks to see if the argument is an array and when not an array adds {} as a singular model. The add method in backbone 0.5.1 is here (0.3.3 functions the same way):

add: function(models, options) {

  if (_.isArray(models)) {
    for (var i = 0, l = models.length; i < l; i++) {
      this._add(models[i], options);
    }
  } else {
    this._add(models, options);
  }
  return this;
},

If you don't pass any arguments to the constructor, you should start with an empty collection.

window.PSPage = new window.PageC();
like image 124
c3rin Avatar answered Nov 15 '22 01:11

c3rin


I was running into the same issue because I needed to pass arguments to my collection constructor. If I look in my collection instance it says there are zero models, but if I iterate using each(), it finds that phantom model that I didn't create.

Based on the accepted answer I went back and looked at the API again here.

My code now looks like this, and it seems to avoid this problem:

var myCollection = new MyCollection(new Array(), {
    setting1: "foo",
    setting2: "bar"
});
like image 38
killthrush Avatar answered Nov 15 '22 00:11

killthrush


From backbone documentation for constructor / initialize of collections:

new Backbone.Collection([models], [options])

This means, that when you want to create new empty collection with some options, you should really call constructor with first argument to be empty array, not your options object (I simplify a bit @killthrush code so you don't type 'new Array()' and use [] instead):

var myCollection = new MyCollection([], {
  setting1: "foo",
  setting2: "bar"
});

Now, in your collection definition, you should have something like to be able to access options object:

var myCollection = Backbone.Collection.extend({
    // add model, url, etc here
    initialize: function (models, options) {
        // use options here, e.g.
       this.setting1 = options.setting1;
       this.setting2 = options.setting2;
    }
});
like image 20
Evereq Avatar answered Nov 15 '22 00:11

Evereq