Search code examples
javascriptbackbone.jsmarionette

Insert rendered view after base element


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'}
]}
...
] 

Solution

  • 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.