Undefined model prototype in Backbone Collection and Marionette CompositeView
Asked Answered
R

1

8

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!

Reclamation answered 20/12, 2013 at 18:4 Comment(5)
Can you include the full code around the line that throws the error to have some context?Lackadaisical
What filename and line number the line with the error at?Artel
Added the code context. @einnocent, the line is 710 in backbone.js.Reclamation
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")Grivet
@BillyChan: That is correct! Actually using 0.9.10. Thanks for the heads up!Reclamation
G
5

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

Grivet answered 20/12, 2013 at 19:32 Comment(3)
Thanks, Billy. I'll check it out and upvote. Regarding the suggestion to not use a model in the CompositeView, would that not be a good pattern if the view is for a model that has a collection plus some metadata associated with it? What would you suggest in that case? Thanks again!Reclamation
@JuanCarlosCoto, I understand your concern. Maybe I'm too opinionated :) If really needed, I myself would prefer to use another name say meta instead of model. I would also prefer to let collection to hold the meta instead of view, because view is not for data, it's for DOM events. For other things it should be stupid.Grivet
Scratch that. The error was an artifact of some change I made in an associated file; this actually solved the problem. Thanks again!Reclamation

© 2022 - 2024 — McMap. All rights reserved.