Marionette.js CompositeView - How to render model and collection without nesting

438 views Asked by At

I'd like to ask for some help because I've been trying to solve this for some time now. I've read Derick Bailey's blog post on tree structures and CompositeViews. I also read David Sulc's but I have what I think is a slightly different use case than the one described there. Note: My project uses Marionette.js 1.0.3.

I am trying to build something that will work like an inbox with emails displayed in a table. An email may be a thread, meaning that it will have other emails that are linked to it. The inbox view is being rendered in a <table> where each <tr> is an email. My JSON looks like:

[
  {id: 1, subject: 'One', threads: []},
  {id: 2, subject: 'Two', threads: [
    {id: 3, subject: 'Three', threads: []},
    {id: 4, subject: 'Four', threads: []}
  ]},
  {id: 5, subject: 'Five', threads: []}
]

My views are configured like this:

InboxView = Marionette.CompositeView.extend({
  // edited for brevity
  initialize: function(options) {
    this.itemView = EmailView;
  }
  // edited for brevity
});

EmailView = Marionette.CompositeView.extend({
  // edited for brevity
  tagName: 'tr',
  initialize: function(options) {
    this.collection = this.model.get('threads');
  },
  onRender: function() {
    if (this.model.isThread()) this.$el.addClass('thread');
  }
  // edited for brevity
});

The issue I'm having is that if I let CompositeView work its magic for me by rendering the model once and then the collection of threads once, I end up with two table rows <tr> (one for each thread) inside the <tr> for the original email (parent).

There exists functionality in the InboxView and EmailView that I'm trying to reuse. What I'm trying to end up with is a table that has all rows shown at the same level.

If you're reading this and want to help me, thank you in advance!

1

There are 1 answers

1
knpsck On BEST ANSWER

First of all you should attach views to the DOM. Errors occur, because child views rendered before they are attached to the DOM. You can override some methods to solve the problem. That will do the trick:

EmailView = Marionette.CompositeView.extend({   

    className: function () {
        return this.model.isThread() ? 'thread' : null;
    },

    initialize: function (options) {
        this.collection = new Backbone.Collection(this.model.get('threads'));
    },

    renderItemView: function(view, index) {        
        this.$el.parent().append(view.el);

        view.render();
    }

});

InboxView = Marionette.CompositeView.extend({

    itemView: EmailView,

    ui: {
        $tbody: 'tbody'  
    },

    renderItemView: function (view, index) {
        this.ui.$tbody.append(view.el);

        view.render();
    }

});

JSFiddle: http://jsfiddle.net/d1krtxtr/