6 ms·
Backbone.js views done right
- voodoomagicman 14y agoWhy not just append all of the views in TableView render, and then have TableRowView set its own ID in its initialize or render method? I think that would be more concise and straightforward.
- superchris 14y agoRead to the end, that's pretty much what I end up doing in the final version. I create the TableRowView in the helper and then listen to the parent views "rendered" event so the row view knows when it's element is in the DOM and it's safe to render itself.
- voodoomagicman 14y agoI did read to the end. What you end up suggesting includes: - rendering the view's elements as html in the parent view - creating all of the views and binding callbacks for all of the views to 'render' on the parent view - triggering all of the callbacks to render the views - each of which pull their element from the first view using a jquery selector. 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.
- jcromartie 14y agoThis is getting way too many votes for what amounts to bad advice. I think you should keep subview rendering methods out of your templates (i.e. keep your templates "dumb"). You can just create placeholder elements in the template, and then pass the placeholder element to the subview constructor. Or you can just append a list of collection-based subviews to a single container element. 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 https://gist.github.com/2931491
- slewis 14y agoHN is about interesting discussion, and this article has generated that, so maybe it has just the right amount of upvotes?
- laserDinosaur 14y agoAssuming people read the comments
- koide 14y agoFWIW, I read the comments first to make a decision whether to read or not the linked article.
- BHSPitMonkey 14y agoPart of the problem is that people will inevitably associate upvotes with a consensus of agreement with the author, and thus trust advice they probably shouldn't.
- rudasn 14y agoNot necessarily. I often find my self upvoting articles that either seem interesting or have interesting discussions in order to go back to them and review them. Most often than not I read the comments first and then the article. 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.
- jashkenas 14y agoWhoa there nellie. Complexity ahoy. 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.collection
- ryanisinallofus 14y ago"Whoa there nellie. Complexity ahoy." Mixing 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.
- jcromartie 14y agoSee: "rocket surgeon", or "enough rope to shoot yourself in the foot". Also: jashkenas wrote Backbone.
- jashkenas 14y agoAw, I'm just horsing around. You're quite right that it's dangerous to mix metaphors while showing someone the ropes.
- dchuk 14y agoI see what you did there
- ryanisinallofus 14y agoLol. This is the kind of shit I'm talking about. In my head jashkenas looks just like this guy when he writes like this: http://upload.wikimedia.org/wikipedia/en/thumb/7/7c/Lulz_Security.svg/200px-Lulz_Security.svg.png http://upload.wikimedia.org/wikipedia/en/thumb/7/7c/Lulz_Sec...
- esmevane 14y agoThis is the pattern I go with: https://gist.github.com/2931439 https://gist.github.com/2931439 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).
- jcromartie 14y agoYes. This is the way to go. I posted pretty much the same thing, but of course it's half as much code in CoffeeScript... time to give that a serious look.
- esmevane 14y agoThis is unrelated to the discussion at hand, but: I'd been writing JS for a while, but it wasn't until I picked up CoffeeScript that I really grasped JS. 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?
- haclifford 14y agoThis is pretty much what we use, here it is with map and builds the element prior to dom insertion (better performance): https://gist.github.com/2932520 https://gist.github.com/2932520
- esmevane 14y agoWell, I'm pretty much going to use this approach from now on. Much cleaner and concise. If you look at the gist history you can see me updating my old code to include fat arrow, and other intuitive-with-experience tricks that I didn't know when I originally drafted the parent code in February. I'm curious - where did you hear that it was better performance? Or is this personal experience?
- rhysbb 14y agoA lot of people are finding out that views are the hardest thing to do - especially with Backbone. The problem is that views are tightly coupled to the model so it's hard to build them up. I just wrote http://modernjavascript.blogspot.com/2012/06/v-in-mvc.html http://modernjavascript.blogspot.com/2012/06/v-in-mvc.html for another post but believe that it could be helpful in this situation. You'd need to actually write a Backbone.controller class and move the logic in to there but then you could create a whole lot of Backbone.Views that were generic and could be hooked up through the Control (I'd also like to see a Backbone.MultiView or something that could take several views in and then re-emit the changes and allow people to add in several small component views in to a large view either for a collection or a model.
- lucisferre 14y agoSorry how they are tightly coupled in backbone? I have a number of beefs with backbone.js but coupling between the model and views (or the views and anything) is not one of them. In fact I mostly only use backbone for the views and the convenient event binding.
- chime 14y agoHow is the performance when each row gets its own object instantiated, its own template loaded, and its own events attached? What happens when you have 1000+ rows in a scrollable/sortable/filterable table? I tried nested views approach but the performance was lacking in slower PCs when the row count was high. 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.
- jashkenas 14y agoYep -- when you're dealing with large numbers of DOM elements, you absolutely don't want to have an individual View for each element. At the other extreme, you don't want to have a single View for your entire application. Find a balance that makes sense, and represents a logical chunk of UI. For a bit more, see the FAQ, particularly the bottom of: http://backbonejs.org/#FAQ-tim-toady http://backbonejs.org/#FAQ-tim-toady
- fooandbarify 14y agoIs there a good rule of thumb as to what constitutes a "large number" of Views? I tend to assign Views to Models rather than Collections, partly because I hate storing Model ids as DOM attributes and extracting them in event handlers (as in the grandparent comment). I've gotten away with several hundred Views on a page before, but that doesn't feel quite right either.
- koblas 14y agoPersonally, I've been more inspired by EmberJS as a template approach. I've gotten the basics working and up and available on github. https://github.com/koblas/distal https://github.com/koblas/distal 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.
- datapimp 14y agoCheck out the Collection GridView in luca. http://datapimp.com/luca/ http://datapimp.com/luca/. Source: https://github.com/datapimp/luca/blob/development-tools/src/components/grid_view.coffee https://github.com/datapimp/luca/blob/development-tools/src/...
- kpozin 14y agoSomething I haven't been able to understand: why does almost every Backbone project generate and update views by applying templates to generate new HTML strings, and then new DOM fragments? 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.
- ianstormtaylor 14y agoI think one of the biggest reasons this is hard to do is because you end up having the same logic in two places. You have to keep the initial $.html() and the incremental changes consistent. 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.
- kpozin 14y agoThe second of those is exactly the approach I've taken in the couple client-side web apps that I've built. I initially generate and insert generic DOM fragments from templates, but only use the template to fill in values that I know will not change throughout the lifetime of the view (the "templates" often don't have parameters). After that, I use one code path both for the initial value setting and for subsequent updates. If I'm not mistaken, this is more or less the approach used in Knockout and AngularJS.
- jashkenas 14y ago> 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.