0
votes

Below is a snippet from a sample tab control from here: http://layout.jquery-dev.net/demos/tabs.html

Those tabs inside those splitter panes are exactly what I need. But I need the tabs to be dynamically added and removed based on user interaction. Thought I could do it with composite or collection views in backbone marionette - add/remove things from the underlying collection and tabs appear and disappear in the UI.

Conceptually this shouldn't hard. But the difficulty comes in with the the dreaded extra wrapping div problem that is so discussed in various threads over the years.

Extra divs in itemviews and layouts in Backbone.Marionette

In my case, each li in my ul comes up wrapped in a it's own div. That messes up the CSS in the code snippet below. It breaks some of it, so things look weird. If I could just produce the output below, things wouldn't look weird.

    <ul class="ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all ui-sortable" role="tablist">
            <li class="ui-state-default ui-corner-top ui-tabs-active ui-state-active" role="tab" tabindex="0" aria-controls="tabs-west-1" aria-labelledby="ui-id-1" aria-selected="true">
                <a href="#tabs-west-1" class="ui-tabs-anchor" role="presentation" tabindex="-1" id="ui-id-1">Nunc tincidunt</a>
            </li>
            <li class="ui-state-default ui-corner-top" role="tab" tabindex="-1" aria-controls="tabs-west-2" aria-labelledby="ui-id-2" aria-selected="false">
                <a href="#tabs-west-2" class="ui-tabs-anchor" role="presentation" tabindex="-1" id="ui-id-2">Proin dolor</a>
            </li>
    </ul>

The output above has complex li's rendered directly in the ul container which is also complex. Why can't I make that output happen?

This extra wrapping div seems to be the default behavior of backbone marionette when itemView's are rendered. Many answers in these threads center on setting the tagName property to li. Problem is that produces a blank, generic li. Look at the li's in the snippet above. They have lots of properties.

My li's will need to have unique id's and styling and other attributes like those above. Making your item view's tagName an li just produces a simple li in your output. How do I get my dynamic id's and styling classes and such in there?

My first attempt to fix this was to pull the li code into my handlebars template so I could encode it with the dynamic data I needed:

    var TabItemView = Marionette.ItemView.extend({
        template: Handlebars.compile(
            '<li class="ui-state-default ui-corner-top ui-tabs-active ui-state-active" role="tab" tabindex="0" aria-controls="tabs-{{id}}" aria-labelledby="ui-id-{{id}}" aria-selected="true">' +
                '<a href="#tabs-{{id}}" class="ui-tabs-anchor" role="presentation" tabindex="-1" id="ui-id-{{id}}">{{tabName}}</a>' +
             '</li>'
        ),
    });

But that now wraps each li in my list in it's own container div. Every combination of using el, tagName, appendHtml override produces either a wrapping element or incorrect results - like appendHtml override causes each new tab to simply replace existing tabs - so I only ever have one tab, the latest one added.

What am I missing here? Why can't I define a template for an li containing dynamic data in it and render it DIRECTLY to my ul WITHOUT an extra wrapping div around each list element? I guess another way to phrase the question is I am working with an existing template that REQUIRES complex li's. How do I produce them in a composite or collection view WITHOUT the extra wrapping container around them?

Backbone and Marionette seem to offer the promise of being able to do things like this in a way more manageable than doing it in straight jQuery. I'm not having that experience. What am I not seeing?

2

2 Answers

0
votes

I don't use Marionette, but I don't see why it would override Backbone's default behavior. This should give you an idea:

var TabView = Marionette.ItemView.extend({
    tagName: 'ul',
    className: 'ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all ui-sortable" role="tablist',
    render: function() {
         //something like this
         var _this = this;
         this.collection.each(function(item){
             var view = new TabItemView({
                 model: item
             });
             _this.$el.append(view.render().el)
         })
     }
});

var TabItemView = Marionette.ItemView.extend({
    tagName: 'li',
    className: 'ui-state-default ui-corner-top ui-tabs-active ui-state-active" role="tablist',
    template: Handlebars.compile(
        '<a href="#tabs-{{id}}" class="ui-tabs-anchor" role="presentation" tabindex="-1" id="ui-id-{{id}}">{{tabName}}</a>'
    ),
});
0
votes

Regarding managing views and data to produce specific HTML output, I suggest you take a look at my blog post here: http://davidsulc.com/blog/2013/02/03/tutorial-nested-views-using-backbone-marionettes-compositeview/

That should get you to structure the views so they render properly. Then, you can add http://backbonejs.org/#View-attributes to the mix to get your complex attributes (for which there aren't existing Backbone attributes such as className).