Rendering Nested Views in Backbone.js

Rendering a Backbone View is simple.  The view’s container element (affectionately known as its el) swaps out all of its html for new html, probably fetched from a template. This works great for any top-level view.

The problem is in the child view. The parent view has swapped out all of its html, including the child view’s container element. The child’s $el is now pointing at a stale element that no longer exists on the page. This means the child is no longer displaying data to the page or getting data from it. The web application is broken, and the programmer sits there frustrated.

Don’t Panic. There are several ways to handle this situation.

1. Don’t Re-Render the Parent View

The simplest is to design a parent view that doesn’t require re-rendering. It has static content and serves as a container of child views. The child view container elements are never re-rendered, so they never go stale. Easy enough to say, but I find this doesn’t cover a lot of situations I encounter.

2. Reassign the Child View’s Element

Another more practical approach is to reassign the child view’s container once the parent view renders. Your view can now re-render appropriately, but its events will still be broken, as they are still stale. This second piece can be fixed with a call to delegateEvents. This method of handling stale sub views is effective and gets right at the heart of the problem. However, I find it messy, hacky, and non intuitive — which leaves me desiring a better solution.

Example.ParentView = Backbone.View.extend({

	childView: new Example.ChildView(),

	render: function() {
		this.$el.html(this.template(this.model.toJSON()));
		this.childView.$el = this.$('#child-view-container');
		this.childView.render();
		this.childView.delegateEvents();
	}
});

3. Use a Marionette.js Region

My favorite way of handling this is with Marionette.js. Marionette addresses several of the pitfalls of Backbone — one of which is the issue of sub views.

Using the Marionette Region object, you can declare a region with an element inside the parent view. To render the child view into the region, invoke region.show and pass in the child view. A view’s regions intuitively understand and react any time the parent view is rendered. This means that when the parent view is rendered, the views that are displayed in its regions update themselves automatically. No reassigning elements or delegate events calls are required. Marionette Layout objects also have a way of defining regions in a hash, much like the backbone events hash. (Edit – in Marionette version 2, Layout was renamed to LayoutView)

Example.ParentView = Marionette.Layout.extend({

	childView: new Example.ChildView(),

	regions: {
		childViewRegion: "#child-view-container"
	},

	initialize: function() {
		this.render();
		this.childViewRegion.show(this.childView);
	}

});

Do you find yourself nesting views frequently? What strategies do you use?

Conversation
  • Muhaimin says:

    could you explain on the marionette layout. I find it hard to follow Derick explanation

    • I use layouts as “view holders”. A layout is a specialized item view. The regions hash in a layout can be thought of as a specialized way to assign elements to sub views. Instead of initializing the sub views by sending them an el, you can show them in a region that already has an element specified. It’s important that the region containers specified by the selectors in the ui hash exist in the template of the layout.

      • Anon says:

        Using setElement on the child view instead of delegateEvents seems to be a nice shorthand method.


        this.$el.html(this.template(this.model.toJSON()));
        this.childView.$el = this.$('#child-view-container');
        this.childView.render();
        //this. this.child.?
        this.childView.delegateEvents();

        turns into


        this.$el.html(this.template(this.model.toJSON()));
        this.childView.setElement(this.$('#child-view-container')).render();

        http://codehustler.org/blog/rendering-nested-views-backbone-js/

        • Thanks for the reply – I didn’t know that one! It’s definitely better than manually invoking delegate events.

        • Dana says:

          You don’t necessarily want to do it this way, though, because this way, you make the parent view totally responsible for the child view’s el, that is to say, you’re moving the child’s logic into the parent, which sort of negates the value of having a separate child view to begin with. I’m not the world’s expert on Backbone, so feel free to arrive at your own conclusions about this, but I thought I’d mention it since I know some very well-respected developers in the Backbone community (Addy Osmani, for one) share this view.

        • Dana says:

          Osmani, by the way, discusses the problem of nested views in a section of his book on Backbone.js, here.

          • Great link and response – thanks for sharing!

            I know that using MarionetteJS isn’t going to work for every project – but I believe that if you have a lot of views and a lot of view hierarchies, Marionette is a tool you want to be using. CollectionViews, Layouts, Regions, and Behaviors make managing views so much less painful.

          • Dana says:

            Yes, I hadn’t really looked at Marionette until recently, but it looks really great. I had a bad experience with Rails and some of its components, so I was really keen on Backbone since it is less opinionated. But more opinionated is OK and can be useful as long as it gets along with my opinion. Thank you for the post!

  • Herlon Aguiar says:

    Thanks a lot!!
    It was too helpful :D

  • Comments are closed.