How to show CompositeView with multiple children v

2019-03-30 02:16发布

The Starting Problem

I have a CompositeView (a table) for which each model in the collection is represented as two table rows, with a template like:

<tr class="row-parent">
    <td>parent info here</td>
</tr>
<tr class="row-child">
    <td>child info here</td>
</tr>

With an ItemView like this:

var ItemView = Backbone.Marionette.ItemView.extend({
    template: ItemTmpl
});

Even though they are named 'parent' and 'child', they are actually peer members of the same model. If I don't specify a tagName, Backbone will wrap each view in a <div> which is both invalid HTML and also breaks the layout.

The First Attempt at a Solution

So I figured, why not remove the outer <tr> tags and let Backbone add them in. So I updated my template to be like:

    <td>parent info here</td>
</tr>
<tr class="row-child">
    <td>child info here</td>

And updated the view to:

var ItemView = Backbone.Marionette.ItemView.extend({
    template: ItemTmpl,
    tagName: 'tr',
    className: 'row-parent'
});

I was hoping that an outer tag would combine with the inner tag fragments, but Marionette didn't like that. It only showed the row-child. So I'm not sure where to go from here. I'm considering two strategies but haven't gone into much details yet.

Moving Forward: Plan A

Override whatever part of Backbone creates the extra div to not create it, or override the part of Marionette which appends the view to remove the div just before appending.

Moving Forward: Plan B

Create a new type of view called CompositeMultiView which, naturally, would extend off CompositeView and allow you two specify a second ItemView, or maybe just an array of views, all of which would be rendered for each model given. This plan seems like a lot more work but less hacked.


Does anyone have any better suggestions, workarounds or concrete pointers as to how I would go about implementing either of the two above plans?

Here is a mockup of what the table should look like: enter image description here

3条回答
聊天终结者
2楼-- · 2019-03-30 02:27

I struggled with that same problem until I finally discovered today, that a table can have multiple tbody tags, each one containing multiple tr tags.

This is actually the answer provided to a similar backbone question.

So your ItemView would become:

var ItemView = Backbone.Marionette.ItemView.extend({
    template: ItemTmpl,
    tagName: 'tbody'
});

And the generated html:

<table>
  <!-- first item -->
  <tbody>
    <tr class="row-parent">
      <td>parent info here</td>
    </tr>
    <tr class="row-child">
      <td>child info here</td>
    </tr>
  </tbody>
  <!-- second item -->
  <tbody>
    <tr class="row-parent">
      <td>parent info here</td>
    </tr>
    <tr class="row-child">
      <td>child info here</td>
    </tr>
  </tbody>
  ...
</table>
查看更多
劳资没心,怎么记你
3楼-- · 2019-03-30 02:38

You could try modifying the CompositeView as follows:

  1. Specify itemView as an array of views
  2. Override addChildView to render each view for each model

This solution ends up looking a lot like your "Plan B". Give it a shot:

itemView: [My.ParentView, My.ChildView],

addChildView: function(item, collection, options){
  this.closeEmptyView();
  var itemViews = this.getItemView(item);
  var index = this.collection.indexOf(item);

  _.each(itemViews, function(ItemView) {
    this.addItemView(item, ItemView, index);
  });
}

I haven't thought through whether this would handle model events such as destroy, but I believe it should handle them gracefully.

查看更多
Fickle 薄情
4楼-- · 2019-03-30 02:43

Dirty solution: Add a custom render function to your ItemView

// Or whatever template you use
var template = Handlebars.compile(datepickerTemplate);

var ItemView = Backbone.Marionette.ItemView.extend({
    render: function(){
        var html = template(this.model.toJSON());
        var newElement = $(html);
        this.$el.replaceWith(newElement);
        this.setElement(newElement);
        return this;
    }
});

This should remove the extra div wrapping

查看更多
登录 后发表回答