3

Has anyone had any success using a vertical table column as a view 'el' using Backbone.js? Since tables are row-based, using a table row as the DOM container associated with the view is trivial. However, the layout I'm required to implement is vertical, so a table column would be the 'el' of my design, but table columns don't have a similar container element since table layout is horizontal.

So far, I haven't been able to successfully create child views for the table columns, which means I'm tying data to my DOM in order to retrieve and manipulate table columns. My design has gotten much more complex, which is causing me to reevaluate the original approach in the hope that there is a better way to manage this scenario through child views. Any ideas?

Jon Neal
  • 33
  • 7
  • 1
    Are you actually presenting tabular data, or using tables for layout? If the latter -- don't. http://stackoverflow.com/questions/83073/why-not-use-tables-for-layout-in-html – jevakallio Jan 21 '13 at 22:27
  • It's possible if your data is being returned in specific format that will adhere to the vertical column format. Obviously, the tbody will be your context and the repeating columns are the elements within. – Dennis Rongo Jan 22 '13 at 07:44
  • Your data has to be in multiple array format. eg, [[{'First column first row', 'First column second'}], [{4567, 'Second column first row', 'Second column second row'}]]. For each column, you loop through each array until you hit the end of the column. – Dennis Rongo Jan 22 '13 at 07:49
  • It is real tabular data - it's a product comparison screen, with each column being a product, and each row a particular attribute for the products. I don't have the ability to modify the design. We need the ability to add or remove products from the table in real-time, which Backbone is well suited for, it's just the vertical layout that kills it. – Jon Neal Jan 22 '13 at 16:02

2 Answers2

3

You could take advantage of the fact that

  • jQuery objects are lists of DOM elements
  • Backbone does not require a unique DOM node (at least as far as I can tell)
  • this.$el in a view can then be whatever nodes you want

With that in mind, you could render your table in one go (see BackboneJS Rendering Problems, it's faster and in your case probably much easier) and then apply your subviews on the table cells associated with the model. I went with a class name for selection but a selector on the nth element of each row should work.

Template

<table>
    <thead>
        <tr>
            <th></th>
            <% _(children).each(function(model) { %>
                <th><%= model.id %></th>                
            <% }); %>
        </tr>
    </thead>
    <tbody>
    <% _(properties).each(function(prop) { %>
        <tr>
            <td><%= prop %></td>
            <% _(children).each(function(model) { %>
                <td class="<%= model.cid %>"><% print(model[prop]); %></td>
            <% }); %>
        </tr>
    <% }); %>
    </tbody>
 </table>

View

ListView = Backbone.View.extend({
    initialize: function(opts) {
        this.options = opts;
    },
    render: function () {
        var data, html, $table, template = this.options.template;

        data = this.collection.map(function (model) {
            return _.extend(model.toJSON(), {
                cid: model.cid
            });
        });

        html = this.options.template({
            children: data,
            properties: ['id', 'name']
        });

        $table = $(html);

        this.collection.each(function (model, ix) {
            var $el = $table.find("." + model.cid),
                subview = new ItemView({
                    el: $el,
                    model: model
                });
        });

        this.$el.empty();
        this.$el.append($table);

        return this;
    }
});

As you can check in this demo http://jsfiddle.net/nikoshr/psasT/12/, each column is handled by a dedicated view.

Community
  • 1
  • 1
nikoshr
  • 32,926
  • 33
  • 91
  • 105
  • Wow, that is a fairly elegant way of managing an 'el' as a collection of elements rather than a single element - I definitely thought of this, but I didn't think it would work. I think I want to go with something resembling this solution. – Jon Neal Jan 22 '13 at 18:38
0

I can't think of a straightforward solution to your problem. Having multiple views share the <table> element as el sounds like a potential nightmare.

How about attacking the problem from a different angle, and instead of using a HTML table, constructing a table columns out of floated <div> elements, and binding the views to those elements? Something like:

<div class="faux-table">
  <div class="faux-column">
    <div class="faux-header">Col 1</div>
    <div>Foo</div>
    <div>Bar</div>
    <div>Baz</div>
    <div>Qux</div>
  </div>
  <div class="faux-column">
    <div class="faux-header">Col 2</div>
    <div>Foo</div>
    <div>Bar</div>
    <div>Baz</div>
    <div>Qux</div>
  </div>
</div>

And a CSS rule:

.faux-table > div.faux-column {
    float:left;
}

A working sample in this fiddle. This solution is of course far from perfect, since if you have variably-sized contents, you'll have to implement the row sizing yourself. But if you need to get the table rendered by Backbone views, this is probably the simplest way from JS point of view.

Edit: Actually, it occurs to me, a far better and less brittle solution is to use table <td> elements as columns, and just implementing the rows as <div>s without any CSS floating hacks:

<table>
    <tr>
        <td class="faux-column">
            <div class="faux-header">Col 1</div>
            <div>Foo</div>
            <div>Bar</div>
            <div>Baz</div>
            <div>Qux</div>
        </td>
        <td class="faux-column">
            <div class="faux-header">Col 2</div>
            <div>Foo</div>
            <div>Bar</div>
            <div>Baz</div>
            <div>Qux</div>
        </td>
    </tr>
</table>

Revised version in this fiddle.

jevakallio
  • 35,324
  • 3
  • 105
  • 112