Something I’ve noticed bouncing around the Backbone community these days is the idea of how to structure subviews. I’m going to outline some approaches for working with Backbone subviews as well as talk about the pros and cons of each.
Note that I really haven’t dug into some of the frameworks that claim to solve this problem, notably Backbone.LayoutManager from Tim Branyen.
For all approaches, I’m going to work with a ParentView
and ChildView
.
var ParentView = Backbone.View.extend({
});
var ChildView = Backbone.View.extend({
});
The ParentView
and ChildView
will have the following templates:
<p>This is a parent</p>
<div id="child"></div>
<p>This is a child</p>
All approaches will also use a Collection
class.
var Collection = Backbone.Collection.extend({
});
I initialize the ChildView
in the ParentView
’s render function. The ParentView
will create a Collection
and pass it to the ChildView
which will render on a Collection reset
event.
var ParentView = Backbone.View.extend({
template: _.template($('#parentTemplate').html()),
initialize: function() {
this.collection = new Collection();
},
render: function() {
this.$el.html(this.template());
this.childView = new ChildView({el: this.$('#child'), collection: this.collection});
return this;
}
});
var ChildView = Backbone.View.extend({
template: _.template($('#childTemplate').html()),
initialize: function() {
this.collection.on('reset', this.render, this);
},
render: function() {
this.$el.html(this.template());
return this;
}
});
This approach more or less makes the assumption that the ParentView
’s render
method won’t be called multiple times. Otherwise, the render
method will need to ensure there is no memory leak concerning this.childView
.
This approach also prevents the ChildView
from using the Backbone.View
tagName
or className
properties.
In this approach, I initialize the ChildView
in the ParentView
’s initialize method, but render it differently.
var ParentView = Backbone.View.extend({
template: _.template($('#parentTemplate').html()),
initialize: function() {
this.collection = new Collection();
this.collection.on('reset', this.renderCollection, this);
this.childView = new ChildView();
},
render: function() {
this.$el.html(this.template());
this.renderCollection();
return this;
},
renderCollection: function() {
this.$('#child').html(this.childView.render().el);
}
});
The big difference between Approach 1 and Approach 2 is that Approach 2 is using ParentView
to set up the reset
event handler. This seems like a disadvantage to me, as the rendering of the ChildView
bleeds into the responsibilities of the ParentView
.
We are able to use Backbone.View
tagName
and className
properties.
The ChildView will simply render it’s template into the default div element
The ParentView
will initialize the ChildView
in it’s initialize method, then let the ChildView
handle its own rendering.
var ChildView = Backbone.View.extend({
template: _.teplate($('#childTemplate').html()),
initialize: function() {
this.collection.on('reset', this.render, this);
},
render: function() {
this.$el.html(this.template());
}
});
var ParentView = Backbone.View.extend({
template: _.template($('#parentTemplate').html()),
initialize: function() {
this.collection = new Collection();
this.childView = new ChildView({collection: collection});
}
render: function() {
this.$el.html(this.template());
this.$('#child').html(this.childView.el);
return this;
}
});
What I like about this approach is that the ChildView
instance is initialized in the ParentView
constructor, the ChildView
is responsible for setting up the collection reset
event handler and rendering itself, re-rendering the ParentView
will not require clean-up code, and Backbone.View
tagName
and className
properties are still usable from the ChildView