The problem is that all Object attributes apart from ‘name’ call the error ‘id/url/whatever is not defined’ in the console when accessed from the template. A template with just ‘name’ displays fine and shows the correct name, but as soon as I call a different attribute, eg. id or url, it breaks. The object passed to the view is a parsed static JSON file with all items sitting on the same level and accessible from the console with e.g. collectionName.models[0].get('id');
What has me confused is that the name attribute works, as if it is predefined somewhere in backbone/underscore code as a default.
Am I missing something very obvious?
Since I can access the model data from the console, I think that there’s something wrong with how the view itself handles the data, but I’ve tried rewriting it in a couple different ways and nothing seemed to make any difference.
All the relevant code.
Passed object format.
This is also what collectionName.models[0].attributes; returns in the console.
[{
"id":"0",
"name": "Building1",
"url": "building_1",
"floors":[{
"id":"0",
"name":"Ground Floor",
"image":"",
"rooms":[{
"id": "r_1",
"name": "Room 1",
},
{
"id": "r_2",
"name": "Room 2"
}]
}
}]
}
Example template code:
<span class="name"><%= name %></span>
<%= id %> <%= url %>
The router code:
routes: {
'': 'intro', // this route is using pretty much identical code and works fine, the model has the exact same format, the only difference is that all attributes work.
':id': 'firstLevel'
},
firstLevel: function (id) {
window.singleBuilding = new ThisBuilding({}, {idBuilding: id});
window.singleBuilding.fetch();
this.floorView = new FloorList({
collection: window.singleBuilding
});
var $intro = $('#intro');
$intro.empty();
$intro.append(this.floorView.render().el);
}
Views:
window.FloorSingleList = Backbone.View.extend({
className: 'floor-list',
initialize: function () {
this.template = _.template(tpl.get('floors-list-item'));
_.bindAll(this, 'render');
this.model.bind('change', this.render);
this.testModel = this.model.attributes; // I tried passing the attributes directly to the templatewithout .toJSON(), which worked exactly the same, as in only the 'name' attribute worked
},
render: function () {
console.log("The test data is:", this.testModel);
console.log("The actual model data is:", this.model);
var renderedContent = this.template(this.model.toJSON());
$(this.el).html(renderedContent);
return this;
}
});
window.FloorList = Backbone.View.extend({
tagName: 'section',
className: 'intro-list',
initialize: function () {
this.template = _.template(tpl.get('intro-list'));
_.bindAll(this, 'render');
this.collection.bind('reset', this.render, this);
this.collection.bind('change', this.render, this);
},
render: function (eventName) {
var $introList;
var collection = this.collection;
$(this.el).html(this.template({ }));
$introList = this.$('.intro-list');
collection.each(function (building) {
var view = new FloorSingleList({
model: building,
collection: collection
});
$introList.append(view.render().el);
});
return this;
}
});
Model code:
window.ThisBuilding = Backbone.Collection.extend({
model : Building,
initialize: function(models, options) {
// Initialising the argument passed on from the router.
this.idBuilding = options.idBuilding;
return this;
},
url : function(){
return "data.json"
},
parse: function (response) {
console.log("Passed parameters are :", this.idBuilding); // Returns the request parameters passed from the router.
return response[this.idBuilding];
}
});
Templates & Bootstrap
// templates are loaded during the bootstrap
tpl.loadTemplates(['header', 'intro-list', 'floors-list-item', 'building-list-item'], function() {
window.App = new ExampleApp();
Backbone.history.start();
});
The problem is in how fetch in javascript is asynchronous…
So what happens is that the render tries to read a collection that hasn’t yet been initialized properly -> your models are not complete yet -> funny readings. Console log works with black magic when regarding to async operations. so it probably tells you something and the reality is something completely different So do this instead:
And then in the
FloorList-view:Update 2: Apparently I saw complexity where there was none… Ignore everything below
From Backbone.js docs, on
Model.toJSON()So it returns the attributes as JSON. Now backbone defines
idasA property, not an attribute. Same goes for url. In a javascript object that represents a backbone Model, the attributes are stored in their own object within the object and the id- and url-properties are stored elsewhere in the model-object. For example the javascript object representing your model could look something like this:
UPDATE: The id-property is embedded onto the JSON in
model.toJSON()So when you do
this.model.toJSON(), you create a JSON from the contents of your model-objects attributes-property and include the id-property. The url -property is not included in this. What you can do for example:and in the template
Hope this helps!