_.bindAll(this) and Uncaught TypeError: Cannot read property 'idAttribute' of undefined in backbone-relation.js
Asked Answered
N

2

6

I have two models (User and Task) which are instances of Backbone.RelationalModel.

The relation about these two models is the following:

// Task model

    var Task = Backbone.RelationalModel.extend({

        relations: [
            {
                type: 'HasOne',
                key: 'user',
                relatedModel: User
            }
        ],

        urlRoot: 'someUrl'

    });

Then I have one collection which code looks like this:

var FollowerCollection = Backbone.Collection.extend({
    initialize: function () {
         _.bindAll(this);
    }
    model: User
});


var User = Backbone.RelationalModel.extend({

});

When I make a fetch on FollowerCollection I get the following error:

 Uncaught TypeError: Cannot read property 'idAttribute' of undefined

on the line 1565 of backbone-relation.js of backbone-relation version 0.5.0


Here a piece of code of backbone-relation.js

if ( !( model instanceof Backbone.Model ) ) {
    // Try to find 'model' in Backbone.store. If it already exists, set the new properties on it.
       var existingModel = Backbone.Relational.store.find( this.model, model[ this.model.prototype.idAttribute ] );

The problem is related to _.bindAll(this) because if I comment it, it works properly.
Why? Any ideas?


Norway answered 29/6, 2012 at 9:57 Comment(9)
Are you sure, that User model defined when you declare the relation?Hendrickson
@mashingan I did attach all code about the Task model. can you give a look? thanks.Norway
How about you attach the code for the FollowerCollection and User so we can even try to find the problem? Currently I see no relation whatsoever between the two classes.Federal
@jakee, mashingan I did attach the code of each model and collection (the main parts);Norway
next up: the code where you fetch the said collectionFederal
@Federal the code FollowerCollection return a collection of users, and it seems ok.Norway
@jakee, thanks to your help. the problem was in collection. I fix it, and I will close this question in few minutes.Norway
@Federal what was wrong about collection was to make _.bindAll(this). can you explain me why it does brake the code? I did change my question title.Norway
well _.bindAll(this) binds this to every function in your Collection, so anything could happen. Most likely it leads to this.model or this.model.prototype being something other than Backbone-relational expects them to. Is the bindAll necessary? If it is necessary for some function in your collection do _.bindAll(this, 'funcname1', 'funcname2', ..., 'funcnameN') to single out the functions you need to bind this to.Federal
G
3

Removing the _.bindAll does work.

It's a shame, because it's a really handy function. It must interact with some part of Backbone badly. I'm on v9.10

I use this method all the time, and issues only come up sometimes (like when you want to do a bulk add to a collection).

For me, The problem was in this Backbone.js method:

// Get a model from the set by id.
get: function(obj) {
    if (obj == null) return void 0;
    this._idAttr || (this._idAttr = this.model.prototype.idAttribute);
    return this._byId[obj.id || obj.cid || obj[this._idAttr] || obj];
},

The code fails at this.model.prototype because prototype is undefined. What? Ya. For reals.

The problem is that when _.bindAll is called, it binds all properties of the collection, as @jakee says. This seems to include Collection.model, which is a bug I think.

The solution is to bind individual methods until this is fixed.

There's an existing, but closed issue on github: https://github.com/documentcloud/backbone/issues/2080 Seems like the current maintainers don't like the method, but I don't understand why.

Gilgamesh answered 20/2, 2013 at 3:21 Comment(0)
C
0

Like my project is really big I had to create my custom bindAll. Here you have the code, it works with the lastest versions. I bind all the properties of the instance "this" except the ones that has prototype with properties, like this.model in a Collection

https://gist.github.com/patrixd/8025952

 //bindAll from underscore that allows 1 argument to bind all the functions from the prototype, 
 //or if there are more arguments they will be the only binded
 _.originalBindAll = _.bindAll;
 _.bindAll = function (that) {
      var funcs = Array.prototype.slice.call(arguments, 1),
          validKeys = [], fn;
      if (funcs.length == 0) {
      for (var i in that) {
          fn = that[i];
          if (fn && typeof fn == "function" && (!fn.prototype ||           
              _.keys(fn.prototype).length == 0))
              validKeys.push(i);
    }
    _.originalBindAll.apply(_, [that].concat(validKeys));
}
else
    _.originalBindAll.apply(_, arguments);

};

Creature answered 18/12, 2013 at 17:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.