Reputation: 33
I am messing around with Backbone some weeks now and made some simple applications based on tutorials. Now I started from scratch again and tried to use the nice features Backbone offers as I am supposed to.
My view gets in the way though. When the page loads, it renders fine and creates its nested views by iterating the collection. When I call render() again to refresh the whole list of just a single entry, all of the views attributes seem to be undefined.
The model of a single entry:
Entry = Backbone.Model.extend({
});
A list of entries: (json.html is placeholder for dataside)
EntryCollection = Backbone.Collection.extend({
model: Entry,
url: 'json.html'
});
var entries = new EntryCollection();
View for a single entry, which fills the Underscore template and should re-render itself, when the model changes.
EntryView = Backbone.View.extend({
template: _.template($('#entry-template').html()),
initialize: function(){
this.model.on('change', this.render);
},
render: function(){
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
View for the whole list of entries which renders a EntryView for each item in the collection and should re-render itself, if a new item is added. The button is there for testing purposes.
EntryListView = Backbone.View.extend({
tagName: 'div',
collection: entries,
events: {
'click button': 'addEntry'
},
initialize: function(){
this.collection.on('add',this.render);
},
render: function(){
this.$el.append('<button>New</button>'); //to test what happens when a new item is added
var els = [];
this.collection.each(function(item){
els.push(new EntryView({model:item}).render().el);
});
this.$el.append(els);
$('#entries').html(this.el);
return this;
},
addEntry: function(){
entries.add(new Entry({
title: "New entry",
text: "This entry was inserted after the view was rendered"
}));
}
});
Now, if I fetch the collection from the server, the views render fine:
entries.fetch({
success: function(model,response){
new EntryListView().render();
}
});
As soon as I click the button to add an item to the collection, the event handler on EntryListView catches the 'add' event and calls render(). But if I set a breakpoint in the render function, I can see that all attributes seem to be "undefined". There's no el
, there's no collection
...
Where am I going wrong? Thanks for your assistance,
Robert
Upvotes: 3
Views: 2797
Reputation: 33344
As is, EntryListView.render
is not bound to a specific context, which means that the scope (this
) is set by the caller : when you click on your button, this is set to your collection.
You have multiple options to solve your problem:
specify the correct context as third argument when applying on
initialize: function(){
this.collection.on('add', this.render, this);
},
bind your render
function to your view with _.bindAll:
initialize: function(){
_.bindAll(this, 'render');
this.collection.on('add', this.render);
},
use listenTo
to give your function the correct context when called
initialize: function(){
this.listenTo(this.collection, 'add', this.render);
},
You usually would do 2 or/and 3, _.bindAll
giving you a guaranteed context, listenTo
having added benefits when you destroy your views
initialize: function(){
_.bindAll(this, 'render');
this.listenTo(this.collection, 'add', this.render);
},
And if I may:
Something like
var EntryListView = Backbone.View.extend({
events: {
'click button': 'addEntry'
},
initialize: function(){
_.bindAll(this, 'render');
this.listenTo(this.collection, 'reset', this.render);
this.listenTo(this.collection, 'add', this.render);
},
render: function(){
var els = [];
this.collection.each(function(item){
els.push(new EntryView({model:item}).render().el);
});
this.$el.empty();
this.$el.append(els);
this.$el.append('<button>New</button>');
return this;
},
addEntry: function(){
entries.add(new Entry({
title: "New entry",
text: "This entry was inserted after the view was rendered"
}));
}
});
var view = new EntryListView({
collection: entries,
el: '#entries'
});
view.render();
entries.fetch({reset: true});
And a demo http://jsbin.com/opodib/1/edit
Upvotes: 4