Phil Giles

backbone child view rendering

I have recently been working on a backbone project which has quite a complicated view structure. This means that the page view holds a child view, say for a table of data, which in itself has another child view for its various components.

This is all made easy with backbone, but when it comes to rendering I have run into a few issues which means that the standard way to render child views isn’t the way for me.

the issue

The problem I ran into seems like quite a simple one, but it took me a while to find the right solution! So my page has two child views. One of them needs to tell the other it’s rendered height so they can both match. This seems simple enough right? Well let’s see the problem I faced:

the current way

Heres a simple example of a page view which initialises two child views, who speak to each other:

var PageView = Backbone.View.extend({
  initialize : function (options) {
    this.sidebarView = new sidebarView({});
    this.tableView = new tableView({'sidebar': this.sidebarView});
    this.holder = $('.container');
  },
  render : function(){
    this.$el.html(this.template());
    this.$el.append(this.sidebarView.render().el);
    this.$el.append(this.tableView.render().el);

    this.holder.append(this.$el);
  }
});

var sidebarView = Backbone.View.extend({
  render: function(){
    this.$el.html(this.template(this.model.toJSON()));
  },
  setHeights: function(height){
    this.el.outerHeight() = height;
  }
});

var tableView = Backbone.View.extend({
  initialize : function (options) {
    this.sidebar = options.sidebar;
  },
  render : function(){
    this.$el.html(this.template(this.model.toJSON()));
    this.sidebar.setHeights(this.el.outerHeight());
  }
});

This all seems to makes sense, but as I mentioned above this has one issue. At the point when the table view calls the sidebar to send its height, the elements haven’t actually been added to the DOM which makes getting their rendered height impossible. All the function would receive is ‘undefined’ as its height. This is due to the fact that the child view passes its HTML back up to the parent which then gets rendered when it needs to.

the solution

Now this solution took me a while to come to, and it might not be perfect but it works!

var PageView = Backbone.View.extend({
  initialize : function (options) {
    this.holder = $('.container');
  },
  render : function(){
    this.$el.html(this.template());
    this.holder.append(this.$el);

    this.sidebarView = new sidebarView({
      'holder': $('.sidebar-wrapper', this.holder)
    });
    this.tableView = new tableView({
      'holder': $('.table-wrapper', this.holder),
      'sidebar': this.sidebarView
    });
  }
});

var sidebarView = Backbone.View.extend({
  initialize : function (options) {
    this.holder = options.holder;
    this.render();
  },
  render: function(){
    this.$el.html(this.template(this.model.toJSON()));
    this.holder.append(this.$el);
  },
  setHeights: function(height){
    this.el.outerHeight() = height;
  }
});

var tableView = Backbone.View.extend({
  initialize : function (options) {
    this.holder = options.holder;
    this.sidebar = options.sidebar;
    this.render();
  },
  render : function(){
    this.$el.html(this.template(this.model.toJSON()));
    this.holder.append(this.$el);
    this.sidebar.setHeights(this.el.outerHeight());
  }
});

What is now happening is the page view is rendered into its holder and then passes the element it would like the child to be rendered inside of from its holder, into the child view.

Now when the table child view runs, its HTML is immediately added to the DOM (this.holder.append(this.$el);) where it can then have its height accessed and passed into the sidebar. This is due to the fact the holders HTML already exists in the DOM so can immediately be accessed.

This approach stops the passing of HTML back up the tree to the parents and instead passes the wrapper down the tree for immediate rendering.

that pretty much it

This is great little solution to a very annoying problem, which I hope can help as many of you as possible! Do you have a better way of fixing this issue? Then let me know in the comments!

If you enjoyed the read, drop us a comment below or share the article, follow us on Twitter or subscribe to our #MetaBeers newsletter. Before you go, grab a PDF of the article, and let us know if it’s time we worked together.

blog comments powered by Disqus