Search code examples
javascriptbackbone.jsmarionette

Turning off div wrap for Backbone.Marionette.ItemView


I'm looking at the Angry Cats Backbone/Marionette tutorial posts here

http://davidsulc.com/blog/2012/04/15/a-simple-backbone-marionette-tutorial/

http://davidsulc.com/blog/2012/04/22/a-simple-backbone-marionette-tutorial-part-2/

and I came upon the same question/need posted here:

Backbone.js turning off wrap by div in render

But I can only get that to work for Backbone.Views, not Backbone.Marionette.ItemViews.

For example, from the simple backbone marionette tutorial links above, take AngryCatView:

AngryCatView = Backbone.Marionette.ItemView.extend({
  template: "#angry_cat-template",
  tagName: 'tr',
  className: 'angry_cat',
  ...
});

The template, #angry_cat-template, looks like this:

<script type="text/template" id="angry_cat-template">
  <td><%= rank %></td>
  <td><%= votes %></td>
  <td><%= name %></td>
  ...
</script>

What I don't like, is that the AngryCatView needs to have

  tagName: 'tr',
  className: 'angry_cat',

-- if I take tagName out, then angry_cat-template gets wrapped by a <div>.

What I would like is to specify the HTML in one place (the angry_cat-template) and not have most HTML (all the <td> tags) in angry_cat-template and a little HTML (the <tr> tag) in AngryCatView. I would like to write this in angry_cat-template:

<script type="text/template" id="angry_cat-template">
  <tr class="angry_cat">
    <td><%= rank %></td>
    <td><%= votes %></td>
    <td><%= name %></td>
    ...
  </tr>
</script>

It just feels cleaner to me but I've been mucking around with Derik Bailey's answer in "Backbone.js turning off wrap by div in render" and can't get it to work for Backbone.Marionette.

Any ideas?


Solution

  • 2014/02/18 — updated to accommodate the improvements noted by @vaughan and @Thom-Nichols in the comments


    In many of my itemView/layouts I do this:

    var Layout = Backbone.Marionette.Layout.extend({
    
        ...
    
        onRender: function () {
            // Get rid of that pesky wrapping-div.
            // Assumes 1 child element present in template.
            this.$el = this.$el.children();
            // Unwrap the element to prevent infinitely 
            // nesting elements during re-render.
            this.$el.unwrap();
            this.setElement(this.$el);
        }
    
        ...
    
    });
    

    The above code only works when the wrapper div contains a single element, which is how I design my templates.

    In your case .children() will return <tr class="angry_cat">, so this should work perfect.

    I agree, it does keep the templates much cleaner.

    One thing to note:

    This technique does not force only 1 child element. It blindly grabs .children() so if you've incorrectly built the template to return more than one element, like the first template example with 3 <td> elements, it won't work well.

    It requires your template to return a single element, as you have in the second template with the root <tr> element.

    Of course it could be written to test for this if need be.


    Here is a working example for the curious: http://codepen.io/somethingkindawierd/pen/txnpE