0

I'm building html tables dynamically depending on the data with Marionette CollectionViews and ItemViews. The structure of the data looks like:

var itemData = [
{
    row: "row 1",
    items: [
        {
            item: "row 1, item 1"
        },
        {
            item: "row 1, item 2"
        }
    ]
},
{
    row: "row 2",
    items: [
        {
            item: "row 2, item 1"
        },
        {
            item: "row 2, item 2"
        }
    ]
}

];

So I have a layout view for the table, a collection view for the rows, another collection view (child of the parent collection view), and item view for the table cells. My code is:

// A Grid Row
var TableDataView = Backbone.Marionette.ItemView.extend({
    template: "#data-template",
    tagName: "td",
    
    initialize: function(){
        console.log('in TableDataView and this.model is ');
        console.log(this.model);
    }
});

// The grid view
var TableRowView = Backbone.Marionette.CollectionView.extend({
    tagName: "tr",

    childView: TableDataView
    
});

// The grid view
var TableBodyView = Backbone.Marionette.CollectionView.extend({
    childView: TableRowView,
    
    childViewOptions: function(model, index) {
        return {
            collection: new Backbone.Collection(model.get('items'))
        };
    },    
});

var LayoutView = Backbone.Marionette.LayoutView.extend({
        template: "#table-template",
        regions: {
            'tableBody': '#table-body'
        },
    
         showChildren: function() {

            this.getRegion('tableBody').show(new TableBodyView({
                collection: this.collection,
            }));
        },
        render: function() {        
            Backbone.Marionette.LayoutView.prototype.render.apply(this, arguments);
            this.showChildren();
            return this;
        }   
});



// ----------------------------------------------------------------
// Below this line is normal stuff... models, templates, data, etc.
// ----------------------------------------------------------------
var itemData = [
    {
        row: "row 1",
        items: [
            {
                item: "row 1, item 1"
            },
            {
                item: "row 1, item 2"
            }
        ]
    },
    {
        row: "row 2",
        items: [
            {
                item: "row 2, item 1"
            },
            {
                item: "row 2, item 2"
            }
        ]
    }
];
    

var Item = Backbone.Model.extend({});

var ItemCollection = Backbone.Collection.extend({
    model: Item
});

var itemCollection = new ItemCollection(itemData);

var layoutView = new LayoutView({
    collection: itemCollection
});

layoutView.render();

$("#table").html(layoutView.el);

The jsfiddle is here. As you can see, the table structure is incorrect because Backbone automatically wraps the TableBodyView in a div wrapper. This then breaks the table HTML. I know on can remove the div from the DOM after render, but I was hoping for a nicer solution...

EDIT

Just to make it clearer, in the TableDataView and TableRowView, i specify tagName to be <tr> and <td> respectively. If I was to do the same in TableBodyView i.e. specify the tagName as <tbody>, I'm still left with the same problem, as i'll have to put a <div id="table-body"> in the table template, which again means the table HTML will still be incorrect...

Community
  • 1
  • 1
Mark
  • 4,428
  • 14
  • 60
  • 116
  • 1
    You've used `tagName` in both the TableDataView and the TableRowView to override the default `div` element, did using it in the TableBodyView not work? – ivarni Aug 26 '15 at 12:36
  • It would work. But then what tag would I give as a region in the table template? – Mark Aug 26 '15 at 13:10
  • 2
    I think what I would do here would be to swap the LayoutView and the TableBodyView for a CompositeView and use the tbody as a childViewContainer. I don't have time to whip up an example right now (bit preoccupied, sorry) but try having a look at the docs and see if you can work something out. If not I might be able to have a look tomorrow. – ivarni Aug 26 '15 at 15:48
  • Actually, the example used in the [docs](http://marionettejs.com/docs/v2.4.2/marionette.compositeview.html#compositeviews-childviewcontainer) happens to be using `childViewContainer` for a `tbody` so there's little point in me repeating that into a fiddle. I don't think there's a better way to get rid of the extra `div`. – ivarni Aug 27 '15 at 11:22

1 Answers1

0

As @ivarni suggested, I modified your fiddle to use a CompositeView in lieu of a LayoutView. According to the Marionette docs, a composite view can be used 'for scenarios where a collection needs to be rendered within a wrapper template.' The wrapper template in this case being the table/tbody.

var CompositeView = Backbone.Marionette.CompositeView.extend({
    template: "#table-template",
    childViewContainer: '#table-body',
    childView: TableRowView,
    childViewOptions: function(model, index) {
        return {
            collection: new Backbone.Collection(model.get('items'))
        };
    }
});

And here is the working fiddle: http://jsfiddle.net/bgyfpe7p/

lucasjackson
  • 1,515
  • 1
  • 11
  • 21