Binding and _.bindAll in backbone.js

backbone.js, javascript, jquery, underscore.js

Solution

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.

Problem

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; } }); ```

Original source