A different approach to rendering Backbone sub-views
I read some good discussion today on Hacker News about how to render subviews in Backbone. There’s no one way to skin this cat, but since I’ve done a fair amount of JavaScript development and have been using Backbone a lot recently, I figured it’s be helpful to put another approach out there since I’ve solved this problem a number of times.
To take the exact problem posited in the blog post, say you have a table that contains a list of names and you want to render the table. You’re planning on having a lot of detail in the individual rows, so it seems like a good idea to split the table view apart from the table row view. Makes perfect sense. Now you want to render the table and you want to delegate to each row to render itself so you’re not interleaving logic. How do you do it?
I put my code below the next few paragraphs because it’s kind of long. Here’s a link to a jsFiddle if you want to play with it: http://jsfiddle.net/N5rUQ/.
Rendering strategy
My strategy for rendering is based on two ideas.
First, it should be intuitive. Event based programming is great and can dramatically simplify things, but only if it’s a conceptual fit. For instance, broadcasting an event saying data has changed and allowing different parts of the UI to react is a great way to decouple code. But, personally, I think of a table as having rows and when a table needs to be rendered, I expect to see the code creating the rows and rendering them in the render method of the table (or not far from it).
Second, JavaScript performance is most impacted by touching the DOM. So you’ll notice I create elements in jQuery, but I never attach them to DOM. I only touch the DOM at the very end when I do this.$el.append(tableEl.children());. That’s a huge performance win. The difference between joining strings and creating elements with jQuery (basically calling document.createElement) is not nearly as significant as the performance hit you get from accessing the DOM repeatedly. I need to find some benchmarks here (or create my own), but I know they exist.
So, when you look at the code below, I hope you find it intuitive. The table creates the sub views for each row, renders them, and then appends that row’s element to a wrapper element that I use to create the final element structure. This is how I’d envision rendering happening in my head, so it’s nice that my mental model matches how the code works.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
|
A few additional comments:
- I’m not using a JS templating engine, instead I’m building HTML with jQuery. If you’d prefer to use something to do the templating, you can see where it would fit in.
- Notice that I’m also opting not to set an
idattribute on the table rows. I really dislike storing information in theidand then having to parse/re-render it constantly.jQuery.datais great for solving this problem and so aredata-*attributes. Curious if anyone has a reason to not do this? The only thing I can think of is it’s hard to find a row quickly from the TableView. That’s why I store thethis.rowViewsmap. It’d be great if browsers could adddata-*selectors to their selector engines. You can do$('tr[data-id="123"]'), but jQuery does most of the work and might have to access a lot of elements to filter it down, so it could be quite slow.
-
EnglishMan
-
Niz