I am confused about binding and the purpose of _bind.All
in backbone.js. Below is a working code that creates a Modal view #modal
and renders out comments fetched from the backend.
Firstly, in the code below, I have in the initialize
function _.bindAll(this, 'render', 'renderComments');
. Whether or not I do _.bindAll()
, I have no problems calling this.render()
and this.renderComments()
inside initialize()
. Are there any examples of when _.bindAll()
will help us and when it will not?
ModalView = Backbone.View.extend({
el: $('#modal'),
template: _.template( $('#tpl_modal').html() ),
initialize: function() {
_.bindAll(this, 'render', 'renderComments');
this.render();
this.renderComments();
},
render: function() {
$(this.el).fadeIn('fast').append( this.template( this.model.toJSON( this.model ) ) );
},
renderComments: function() {
this.commentList = new CommentCollection();
var self = this;
this.commentList.fetch({
data: { post_id: this.model.id},
processData: true,
success: function() {
self.commentListView = new CommentListView({ collection: self.commentList });
}
});
}
});
And
CommentListView = Backbone.View.extend({
el: '.modal_comments',
initialize: function() {
this.render();
},
render: function() {
var self = this;
this.collection.each( function(comment, index) {
$(self.el).append( new CommentListItemView({ model: comment }).render().el );
});
return this;
}
});
Secondly, I am confused about prepending this.
to something. For example in renderComments
, why cant I use:
var commentList = new CommentCollection();
var self = this;
commentList.fetch({.... });
For the line this.commentList = new CommentCollection();
, other than instantiating the class CommentCollection()
, does it make commentList
a child of ModalView
?
Additionally, is it necessary to have var self = this;
and use self.commentListView
later in the callback function? Can binding be used so I can access this.commentListView
, or is using var self = this
the conventional way of doing things?
Finally, should self.commentListView = new CommentListView({ collection: self.commentList });
in the success function of renderComments
be moved to CommentListView
's initialize method instead and be binded to this.collection.on('reset');
to prevent nesting too many functions? This will result in:
ModalView = Backbone.View.extend({
el: $('#modal'),
template: _.template( $('#tpl_modal').html() ),
initialize: function() {
_.bindAll(this, 'render', 'renderComments');
this.render();
this.renderComments();
},
render: function() {
$(this.el).fadeIn('fast').append( this.template( this.model.toJSON( this.model ) ) );
},
renderComments: function() {
this.commentList = new CommentCollection();
this.commentListView = new CommentListView({ collection: this.commentList });
this.commentList.fetch({
data: { post_id: this.model.id},
processData: true
});
}
});
CommentListView = Backbone.View.extend({
el: '.modal_comments',
initialize: function() {
this.collection.on('reset', this.render, this);
},
render: function() {
var self = this;
this.collection.each( function(comment, index) {
$(self.el).append( new CommentListItemView({ model: comment }).render().el );
});
return this;
}
});
phew--long question(s) ;)
1) I used to do _.bindAll
in my initialize methods when I was first using backbone but I have since stopped. It's not normally needed unless you're binding to events and then it's really helpful. For example if you have:
events:
{
'click': clickHandler
},
clickHandler: function(){
//do cool stuff
}
then it's helpful to do _.bindAll(this, 'clickHandler')
otherwise your this
pointer won't be the view
2) If i understand your question: commentList
becomes a property of your instance of ModalView
.
3) using var self = this;
is relatively common, but in many cases can be avoided because of overloads in Underscore.js (which is a dependency of backbone.js). For instance, most of the collection functions (map
, each
, etc) take a context as the last parameter. so instead of
var self = this;
_.map([1,2], function(item){
self.sum = self.sum + item;
});
you can do:
_.map([1,2], function(item){
this.sum = this.sum + item;
}, this);
if your case you could replace your success
method with
success: _.bind(function() {
this.commentListView = new CommentListView({ collection: this.commentList });
}, this);
If you want more info on the somewhat confusing subject of this pointers, it's suggest the following excellent tutorial: http://bonsaiden.github.com/JavaScript-Garden/#function.this
4) Yes--I would move the rendering to the reset
. that way if something else causes a reset of the collection the view will pick it up.
Hope I answered all your questions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With