8
votes

Trying to populate a Collection from a list of values, I am getting an error about the Collection's model's prototype being undefined. Looking at this question about a similar problem, I have checked that the Model is actually created before the collection is instanced, to the best of my ability.

The error is being thrown in one of the event handlers of the Marionette CompositeView that holds the Collection, after fetching the data from the server and trying to reset the collection with the list of values from the data which should be populated into it.

Note: Using Backbone 0.9.10

The Model

MyItemModel = Backbone.Model.extend({});

The Collection

MyCollection = Backbone.Collection.extend({
    model: MyItemModel
});

The CompositeView's relevant code

MyCompositeView = Backbone.Marionette.CompositeView.extend({

    initialize: function(options) {
        _.bindAll(this);
        this.model = new MyCompositeViewModel();
        this.collection = new MyCollection();
    },

    //This event handler gets properly fired and run.
    on_event: function() {
        var that = this;

        // The data comes through fine with this `fetch`
        this.model.fetch({success: function() {
            var collection_results= that.model.get("collection_results");

            // The error fires in this line
            that.collection.reset(collection_results);
            that.render();
        });
    }
})

The error

The error happens in the add function in Backbone, when doing a get for the model object, checking to see if it is a duplicate. The failing code is here:

// Get a model from the set by id.
get: function(obj) {
    if (obj == null) return void 0;

    // The error originates from this line
    this._idAttr || (this._idAttr = this.model.prototype.idAttribute);
    return this._byId[obj.id || obj.cid || obj[this._idAttr] || obj];
},

this._idAttr || (this._idAttr = this.model.prototype.idAttribute);

Here, the this.model.prototype.idAttribute fails because the prototype for the model is not defined.

Why is this happening, and how can it be fixed?

Thanks a lot!

1
Can you include the full code around the line that throws the error to have some context?Sergio Ayestarán
What filename and line number the line with the error at?einnocent
Added the code context. @einnocent, the line is 710 in backbone.js.Juan Carlos Coto
There is no such code as you referred in Backbone 0.9.2. github.com/jashkenas/backbone/blob/0.9.2/backbone.js Are you sure? (Just search the page with "_idAttr")Billy Chan
@BillyChan: That is correct! Actually using 0.9.10. Thanks for the heads up!Juan Carlos Coto

1 Answers

5
votes

The reason is, in Babkbone 0.9.10, if you call collection.reset(models) without options, the models will be passed to collection.add() which strictly needs real models as argument.

But, in fact, the arguments you passed are not real models. They are just an array of hash attributes.

Two options to fix:

Option 1: Call the reset with a parse option

that.collection.reset(collection_results, {parse: true});

Then reset will parse the array of hashes and set them as model.

Option 2: Upgrade to latest version Backbone 1.1.0.

Here reset() no longer pass responsibility to add() but use set() smartly. This option is recommended. And you don't need options here.

that.collection.reset(collection_results)

Another point

May I suggest you not to define model in CompositeView? CompositeView is for collection, not model. Of course I understand the model here is just to hold and fetch some data, but it would be really confusing for the code to be read by another developer, as well as your own maintaining.

To get bootstrapped data, you can load the data at first request and use conventional way to put it into collection. http://backbonejs.org/#FAQ-bootstrap