Backbone.js views done right
blog.gaslightsoftware.com
blog.gaslightsoftware.com
Why would you want to have a view that dumps out its HTML as a string? If a string of HTML is all you want, don't use a view, just use a template.
But putting that aside for a second ... for this particular example, how about this:
render: ->
this.$el.html JST["table_view_template"]()
tbody = this.$el('tbody')
for person in this.collection.models
view = new TableRowView model: person
tbody.append view.render().el
... or if you were in earnest about only needing the raw HTML from the sub-view, how about just rendering the row templates within the table template, making your render function as simple as this: render: ->
this.$el.html this.template people: this.collectionMixing metaphors? Is it a horse or a ship you are riding?
It's really hard to read the rest of your technical reasoning (which makes sense) with opening statements like that.
Also: jashkenas wrote Backbone.
And if you want to delegate the handling of these DOM lines to sub-views (for event handling), you can instantiate them after rendering and call `View#setElement` with a row each.
What I prefer to do is the following:
class Example.Views.TableView extends Backbone.View
events:
'click td': 'clicked'
rowHtml: ->
x = []
p = $('#person_row').html()
for person in @collection.models
x.push(_.template(p, person))
x.join('')
clicked: (e) ->
id = $(e.target).parents('tr').attr('id')
# handle action for row id
render: ->
# render self view including rowHTML
The template is loaded just once into a variable and filled in each iteration. Only one set of events is attached to the DOM. Only one call is made to DOM to create all the rows, one string merged from HTML array. I still get the benefit of view and subview templates, without the constructor for subview objects being called 1000 times when all I need is the <tr> HTML x 1000.For a bit more, see the FAQ, particularly the bottom of: http://backbonejs.org/#FAQ-tim-toady
You should be able to write Backbone apps without resorting to stuff like $("#row_#{@model.id}")
And for an example, here's a gist https://gist.github.com/2931491
If someone would implement some code pattern depending on the number of votes that pattern has on HN they have a bigger problem than how HN voting works.
Wouldn't it be much more efficient to create a view just once by setting innerHTML, and then update it using the DOM (setting attributes, classes, innerText, etc.)? Surely this would reduce GC pressure, reflow events, and so on.
I'm seeing "use templates for everything" in most Backbone tutorials and projects, and I can't help but think that this is an anti-pattern.
Or you could have the incremental changes follow the first call t $.html(), but then you always have to add those in each render and it gets ugly there too.
If you have other ideas though I'd love to hear 'em.
If I'm not mistaken, this is more or less the approach used in Knockout and AngularJS.
> Wouldn't it be much more efficient to create a view just
> once by setting innerHTML, and then update it using the
> DOM (setting attributes, classes, innerText, etc.)?
Funnily enough, it usually isn't. Especially in older browsers, where performance matters most -- setting a single innerHTML call with a bunch of HTML is far cheaper than doing the equivalent number of DOM-twiddling operations.While it's entirely possible with Backbone views to listen to specific change events and only modify the smallest possible portion of the DOM, it's usually not worth the bother -- appropriately coarse-grained template renders are both more convenient and faster.
While I don't yet have a fancy blog post about the system and why it's "better" I would be interested if people think there is some core value in this style of View abstraction for Backbone.
It decouples a little more logic from the template, and I think it adheres very closely with some things Backbone expects (like passing through the entire el to the DOM).
I know that sounds strange, but CoffeeScript taught me important concepts in JS which I just couldn't grasp beforehand. Prototypal inheritance, scoping, object construction, and use, etc.
I'm a pretty big advocate at this point. Contrary to what a lot of its detractors say, CoffeeScript insinuated in my mind most of the JS best practices. Going back to JS from CoffeeScript, I found JS just as easy to correctly write and use as CoffeeScript had ever been.
How about that?
I'm curious - where did you hear that it was better performance? Or is this personal experience?
Found this S.O thread http://stackoverflow.com/questions/10296791/backbone-js-perf... (different code, same concept) and decided to benchmark: Unqueued DOM insertion: http://jsfiddle.net/arkxp/9/ Queued DOM insertion: http://jsfiddle.net/arkxp/8/
The latter is consistently faster, and while the difference isn't a lot with this example, given a more complex insertion it'll mount up.
There's at least two projects I have going at the day job which have been experiencing slowdown - and I had no idea that small-batch DOM interaction was the culprit.
Looks like I've got some optimizin' to do.
I think that would be more concise and straightforward.
I guess the advantage is that re-rendering the parent will re-render the subviews instead of re-creating them? Unless you need that, it seems to me like this is a lot of mess compared to just adding @$el.attr 'id', "row_#{@model.id}" to the child view.