0

So I have a compositeView. CompositeView is an "ul" element with base first "li" element and a collection of another "li" elements. My objective is to insert each rendered child collection of elements after base "li" element.

My views code

var
_NavItem = Marionette.ItemView.extend({

    tagName: 'li',
    template: function (serialized_model) {

        return _.template('<a href="/"><span class="<%= name %>"><%= name %></span></a>')(serialized_model);
    }   
});

var
_NavComposite = Marionette.CompositeView.extend({

    tagName: 'li',
    className: 'header',
    childView: _NavItem,
    initialize: function () {

        this.collection = new Backbone.Collection(this.model.attributes.items);
    },
    template: function (serialized_model) {

        return _.template('<%= name %>')(serialized_model);
    },
    attachHtml: function(collectionView, childView){

        collectionView.$el.append(childView.el);
    }
});

var
_NavigationView = Marionette.CollectionView.extend({

    tagName: 'ul',
    childView: _NavComposite
});

and in fact this code will render structure

ul
    li (base0) /li
    li /li (li of collection)
    li /li (li of collection)
    ...
    li (base1) /li
    li /li (li of collection)
    li /li (li of collection)
    ...
    li (base2) /li
    li /li (li of collection)
    li /li (li of collection)
    ...
/ul

but when I start this nothing happend. If I change code to

attachHtml: function(compositeView, childView){

    compositeView.$el.append(childView.el);
}

it works fine, but this render another structure, that I want

ul
    li (base0)
        li /li (li of collection)
        li /li (li of collection)
        ...
        li /li (li of collection)
    /li
    li (base1)
        li /li (li of collection)
        li /li (li of collection)
        ...
        li /li (li of collection)
    /li
    ...
/ul

Structure of data

[0: { id: 1, name: 'Base li name 1', items: [ 
    0: {id: 2, name: 'Li child 1'},
    1: {id: 3, name: 'Li child 2'}
]}, 
1: { id: 4, name: 'Base li name 2', items: [ 
    0: {id: 5, name: 'Li child 3'},
    1: {id: 6, name: 'Li child 4'}
]}
...
] 
Evgeniy
  • 3,219
  • 5
  • 25
  • 40

2 Answers2

2

UPDATE TO CHANGED QUESTION:

Because of the view wrappers generated by Backbone.View, there is no way to get exactly the structure you want.You could at best have multiple li's each with li (base) and sibling li (collection) items, but they would exist in a second level list. Like this,

<ul>
   <li>
      <ul>
         <li>
            composite0.model.name
         </li>
         <li>
            childView0.model.name
         </li>
           .
           .
           .
         <li>
            childViewN.model.name
         </li>
     </ul>    
  </li>
    .
    .
    .
  <li>
      <ul>
         <li>
            compositeN.model.name
         </li>
         <li>
            childView0.model.name
         </li>
           .
           .
           .
         <li>
            childViewN.model.name
         </li>
      </ul>
   </li>
</ul>

I think all you really need is one CompositeView, and you can leverage the CompositeView template to get the effect you're looking for:

var _NavItem = Marionette.ItemView.extend({
    tagName: 'li',
    template: function (serialized_model) {

        return _.template('<a href="/"><span class="<%= name %>"><%= name %></span></a>')(serialized_model);
    }   
});

var _NavBaseListView = Marionette.CompositeView.extend({

        tagName: 'ul',
        className: 'header',
        childView: _NavItem,
        template: function (serialized_model) {
            return _.template('<li><%= name %></li>')(serialized_model);
        },
        attachHtml: function(collectionView, childView){        
            collectionView.$el.append(childView.el);
        }
});

How it works

The template on the CompositeView will return

<li>
   composite.model.name
</li>

and when attachHtml in the CompositeView is invoked, the childView, which is another <li> will simply be appended below the base <li> described in the CompositeView template. (BTW, you don't have to override attachHtml at all, since collectionView.$el.append is the default behavior [well sort of, because it buffers the children and then appends the group, but it's the same result]).

At the end you'll end up with

<ul>
   <li>
      composite.model.name
   </li>
   <li>
      childView0.model.name
   </li>
     .
     .
     .
   <li>
      childViewN.model.name
   </li>
</ul>

In fact this is one of the reasons CompositeView was built in the first place, see Composite Views: Tree Structures, Tables, And More, see the Grid Views part.

seebiscuit
  • 4,905
  • 5
  • 31
  • 47
  • Of course you are right, thanks for your answear. But if base "li" more then 1, it will not work. – Evgeniy Nov 22 '15 at 16:54
  • That is true, but then, in your question you didn't specify that you needed more than one base li. That's one of the big shortcomings I found with Marionette--you can't have more than one collection in a collection view... – seebiscuit Nov 22 '15 at 16:56
  • It is my mistake and I'am so sorry. I update my post again, hope this addition will be useful. – Evgeniy Nov 22 '15 at 16:59
0

I think the heart of the problem is that the structure you are proposing is not valid HTML. From MDN:

The HTML <li> element (or HTML List Item Element) is used to represent an item in a list. It must be contained in a parent element: an ordered list (<ol>), an unordered list (<ul>), or a menu (<menu>). In menus and unordered lists, list items are usually displayed using bullet points. In ordered lists, they are usually displayed with an ascending counter on the left, such as a number or letter.

When you nest <li> elements the proper structure is to have each new level of the list to be wrapped in a <ul>.

Either you should be using divs for your structure or wrap nested <li>s in a <ul>.

If you choose to change your structure than it would be much easier for Marionette to accommodate that.

dchapman
  • 365
  • 5
  • 20
  • Of course in the end of view render, html structure must be valid and if .after function worked we can see it. But @seebiscuit was right when talk "Marionette only attaches the view wrapper to the view". That is why I will use another data or html structure. – Evgeniy Nov 23 '15 at 12:39