I have a web application that is currently using backbone.js 0.5.3 with backbone local storage adapter 1, underscore.js 1.2.2 and jquery mobile 1.0.1.
I wanted to upgrade my version of backbone and underscore, however when I upgrade my version of underscore (1.2.2 to 1.3.1) I get an error in backbone.js (I’ll detail the error below). I tried just upgrading underscore and upgrading both underscore and backbone.js and I still get the error. I don’t see any breaking changes in the changelog for underscore, but maybe I’m missing something.
Here are the details of the javascript error that I’m getting in backbone.js, the error message is “this.model is not a constructor“ in firebug or “Uncaught TypeError: undefined is not a function” in chrome. The error is thrown in the _prepareModel method of the backbone.collection which is called by its add method which is triggered off based on a fetch call in my code (which is pulling from local storage not the server). What I can see is that “model” is undefined (I think it’s supposed to be a function) in the method and that’s why it’s throwing the error, I’m just not quite sure what to do about it.
Here’s the code of the _prepareModel method
_prepareModel: function(model, options) {
if (!(model instanceof Backbone.Model)) {
var attrs = model;
options.collection = this;
model = new this.model(attrs, options);
if (!model._validate(model.attributes, options)) model = false;
} else if (!model.collection) {
model.collection = this;
}
return model;
},
I don’t really have much of my own code that I can post, since I’m just calling fetch on a collection.
Thanks
EDIT
Here’s some code that reproduces the issue I’m talking about. If i use underscore.js version 1.2.2 it works fine, if i swap it for 1.3.1 it throws an error.
var YM = YM || {};
YM.Holidays = YM.Holidays || {};
YM.Holidays = {
Holiday: Backbone.Model.extend({
fDate: null,
Description: null,
}),
HolidayView: Backbone.View.extend({
tagName: 'li',
holidayTemplate: null,
initialize: function () {
this.holidayTemplate = _.template($('#holiday-template').html());
},
render: function () {
$(this.el).html(this.holidayTemplate(this.model.toJSON()));
return this;
},
}),
Holidays: Backbone.Collection.extend({
model: YM.Holidays.Holiday
}),
HolidayListView: Backbone.View.extend({
el: 'body',
initialize: function () {
this.collection = YM.holidays;
},
render: function () {
var $holidayList = $('#ulHolidays');
_.each(this.collection.models, function (holiday) {
var view = new YM.Holidays.HolidayView({ model: holiday});
$holidayList.append(view.render().el);
}, this);
}
})
};
$(function () {
var holidays = [ {fDate: '1/1/2012', Description: 'New Years'}
, {fDate: '1/16/2012', Description: 'ML King Day'}
, {fDate: '2/20/2012', Description: 'Presidents Day'}];
YM.holidays = new YM.Holidays.Holidays();
YM.holidayListView = new YM.Holidays.HolidayListView();
YM.holidays.add(holidays);
YM.holidayListView.render();
});
EDIT 2
It gets a bit stranger, it seems like the issue is somehow related to jquery mobile, I plan on looking into that a little more. In the meantime here are two jsfiddles the only difference between them being which version of underscore they use.
http://jsfiddle.net/KDMcd/ – underscore 1.2.2 works
http://jsfiddle.net/hnGAA/ -underscore 1.3.1 doesn’t work
EDIT 3
Thanks, that works in my test example and I’m going to try that in my actually project. Do you have any idea why it’s necessary to explicitly set the model later? Because the way I was doing it seems to be the way it’s supposed to be done based on the documentation. Also any idea what changed in underscore that this is now necessary? Thanks
OK, I was able to fix the issue based on ryanmarc answer, what I ended up doing is explicitly setting the backbone’s collection’s models in the initialize method so instead of
Holidays: Backbone.Collection.extend({
model: YM.Holidays.Holiday
}),
I’m doing this
Holidays: Backbone.Collection.extend({
inititlaize: function ()
{
this.model = YM.Holidays.Holiday;
}
}),
Another strange thing is using the same code and libraries if I switch the html so that I’m rendering the list in a plain div as opposed to a JQM page it also doesn’t throw that error (it renders the list twice for some reason, but since I’m not really trying to do that I haven’t looked into why).
For example if instead of the following markup
<div data-role="page" id="Holidays">
<div data-role="header">
<h1>Holidays</h1>
</div>
<div data-role="content">
<ul data-role="listview" data-inset="true" id="ulHolidays">
</ul>
</div>
<div data-role="footer">
<h3>Footer</h3>
</div>
</div>
I use this mark up
<div>
<ul data-role="listview" data-inset="true" id="ulHolidays"></ul>
</div>
I don’t get the error (though it renders twice, but that’s probably not related). Anybody have any ideas or insights as to what is going on here?
it works fine with this small change: http://jsfiddle.net/hnGAA/1/