In Backbone.js, rendering views is really simple, yet not so much. It’s simple because Backbone doesn’t force you into doing it any specific way, so you have freedom to just use a bit of jQuery and dump the HTML into an element. Then again, since it doesn’t implement anything on its own, we’re stuck writing our own implementations, making it more difficult than it could otherwise be. In the case of rendering subviews, things can definitely get a little more difficult.
I know I said I was going to put off doing any more Backbone.js stuff for a while, but I had to do it. First, I read about this trick on Ian Storm Taylor’s blog and decided it was worthy of spreading. Secondly, I’m not prepared for writing about the topic I planned on doing next, so it needs to wait a bit.
In Ian Taylor’s original post about the trick, he opened up by listing some requirements that should be met for subview rendering implementation. First, though, you have to fully understand what’s going on when we talk about subviews.
If you look back at the Wine Cellar application that we created in my Backbone.js screencast series you’ll see that the sidebar is built from a single view that creates additional views for each list item. This is not what we’re talking about. If you take the entire body section from that application, create a view that manages all three view areas (header, sidebar, and main), then those three areas would be the type of subviews that we’re talking about. The main view would contain a reference to each of the three views and render them in their correct places. So rather than using the Router to set the views, like we did in the Wine Cellar app, we would use a super view to set up the other views.
Now that we’re on the same page, let’s take a look at the requirements that Ian had:
rendershould be able to be called multiple time without side effects. Often, the “current way” of doing things will break event listeners on subviews. Anything breaking like that is a side effect.
renderagain should maintain the state the view was in. If the state hasn’t changed, then calling render on any view (super or sub) should not cause any changes to what is already rendered.
- _Rendering twice shouldn’t trash views just to reconstruct them again. _This one is pretty self-explanatory. Don’t remake a perfectly fine subview. If it’s in the state that you want it to be in, just leave it be.
First let’s take a look at how someone might normally do this:
Note that this code assumes that the
render method of the subviews always returns the views
el, just like this outer
render function does. I prefer my code to do this. I’ve seen a lot of people just return
this. That makes sense if you want to make things chainable, but 95% of the time you just end up writing this:
This is ugly (in my opinion) because you reference a property after a function. If you already have a reference to the view and if 95% of the time you’re just going to ask for
el right away anyway, why don’t we just simplify it a bit and return
this.el from the
Anyway, back to the first code snippet. You may not realize it, but this has a serious flaw in it. When you call jQuery’s
html function, jQuery will first call
empty on the current contents, removing all the bound event handlers on the elements. When you call
render on your subviews, those events won’t be re-bound, so you’re stuck with static HTML and no event listeners.
One way you could fix this is to call
delegateEvents() within every
render function of the subviews, but that’s just one more step you have to remember to include in every subview. Some people will instead just recreate the subviews, which causes too much overhead and useless computation.
Mr. Taylor notes that using
setElement on the subviews works really well. When you call
setElement, the argument passed in will become the new element for the subview (replaces
this.el within the subview). It also causes
delegateEvents to be called again in the subview so the event listeners are reassigned. So our
render function would now look like this:
This becomes a bit annoying to manage, though, so he created a function that he adds to the outer view that he calls
assign, which would look like this:
Then he just uses
assign within his
This then takes care of all the requirements he has, but he wasn’t satisfied with this. He later wrote a second post on the subject where he states that he took a look at Layout Manager and saw that it was using the same concept. But it showed Ian a way to improve his
assign function a bit, which would change the last code snippet we wrote into this:
It’s a minor improvement that makes it less repetitive by requiring you to only need to call
assign once. Here’s what the new
assign method looks like:
Thanks a bunch Ian Storm Taylor for your insights! I’ll definitely be using this in my smaller applications, but when I get into slightly larger apps, I think I’ll take a deeper look at Backbone.LayoutManager. Do you have any really cool “tricks” that you use in your Backbone.js applications? Share them in the comments below! God bless and happy coding!