Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lack of childViewContainer in Marionette 3 makes valid table layouts difficult. #3150

Closed
jgerigmeyer opened this issue Aug 30, 2016 · 16 comments

Comments

@jgerigmeyer
Copy link

jgerigmeyer commented Aug 30, 2016

Description

I have a table layout (<table>) where I would like each item in a collection to render as a <tbody>. For example, the goal is markup such as:

<!-- for a collection with two models -->
<table>
  <thead>
    ...
  </thead>
  <!-- first model -->
  <tbody>
    <tr>
      ...
    </tr>
    <tr>
      ...
    </tr>
  </tbody>
  <!-- second model -->
  <tbody>
    <tr>
      ...
    </tr>
    <tr>
      ...
    </tr>
  </tbody>
</table>

In Marionette 2, I was able to use a CompositeView:

const Item = Marionette.ItemView.extend({
  tagName: 'tbody',
  template: '#item'
});

const List = Marionette.CompositeView.extend({
  template: '#list',
  childViewContainer: 'table',
  childView: Item
});

With the following #item template:

<tr>
  ...
</tr>
<tr>
  ...
</tr>

And the following #list template:

<table>
  <thead>
    ...
  </thead>
  <!-- collection will be appended here -->
</table>

Because I used the table as the childViewContainer, the children were simply appended.

With CompositeView now being deprecated in Marionette 3, I'm not sure how to get the same end result without adding an extra layer. And I can't really add an extra layer if I want valid table HTML. From reading the documentation, it seems like the approved new way to do this is to use a View wrapper that shows a CollectionView in a region (probably using replaceElement: true). But unless I'm missing something, that still adds one layer (because the CollectionView needs an el of its own). The examples in the docs assume that the CollectionView can be a tbody, and the individual items can be trs. But if I need the items to be tbodys, I need a way to render the collection without any wrapper of its own.

It seems like the concept of a CollectionView that both renders a template and renders children in a childViewContainer has been removed in Marionette 3, without a good replacement. I suppose I could override the CollectionView render function to render a template as well as the children?

What am I missing here? Is there a recommended way to do table layout like this?

Environment

  1. Marionette version: 3.0.0
  2. Backbone version: 1.3.3

Update: You can see the way I was doing this with Marionette v2--along with my attempt to upgrade to Marionette v3--here: https://jsfiddle.net/jgerigmeyer/L3ttz0k4/

@blikblum
Copy link
Collaborator

@jgerigmeyer
Copy link
Author

jgerigmeyer commented Aug 31, 2016

@blikblum Yes, I read that (and linked to it in my description). It doesn't address a case with no wrapper around the list of children. Thanks though!

@rafde
Copy link
Member

rafde commented Aug 31, 2016

@jgerigmeyer you brought up an interesting problem. Could you set up a fiddle for this, please? I'm thinking about using document.createDocumentFragment() for the el and something else that I am kicking around in my head.

@blikblum
Copy link
Collaborator

Sorry not reading your issue carefully

@jgerigmeyer
Copy link
Author

jgerigmeyer commented Aug 31, 2016

@rafde I added a jsfiddle to the description.

@paulfalgout
Copy link
Member

Use CollectionView#addChildView
https://jsfiddle.net/L3ttz0k4/3/

@jgerigmeyer
Copy link
Author

@paulfalgout Won't that cause indexing bugs with a sorted collection if models are added later? It also adds complexities with sorting/reordering, changing the filter, etc.

https://jsfiddle.net/jgerigmeyer/L3ttz0k4/5/

@rafde
Copy link
Member

rafde commented Sep 1, 2016

this issue is looking related to #3128 where

<table>
  <thead>
    <!-- don't remove me, bro -->
  </thead>
  <!-- collection will be appended here -->
</table>

@jgerigmeyer
anyhow https://jsfiddle.net/yj2engmc/1/

@paulfalgout
Copy link
Member

@jgerigmeyer for this fiddle you can use onRenderChildren https://jsfiddle.net/L3ttz0k4/6/

but yes.. you will have to manage the view added in regards to sorting / filtering

But in this case collectionView sorting/filtering will work without any effort.

you should be able to add/remove to the collection with sorting..

also

list3.viewComparator = 'thing2';
list3.resortView();

will work as well as

list3.setFilter(function(child){ return child.get('thing1') !== 'Baz'; });

If you ever get a more complicated addition you might need to handle the index of the added view inside the filter.. or possibly do some more complicated switcheroos for maintaining a sort order (like say you wanted insert headings every 50 rows)

but in those cases I don't think CompositeView is any better anyhow.

@paulfalgout
Copy link
Member

Just dropping a brief code comparison for visuals. I think this supports what is being asked:

In v3

const List = Mn.CompositeView.extend({
  childView: Item,
  onRenderChildren () {
    this.addChildView(myHeadView, 0);
  }
});

In v2:

const List = Mn.CompositeView.extend({
  template: '#list',
  childViewContainer: 'table',
  childView: Item
});

@paulfalgout
Copy link
Member

@jgerigmeyer is this resolved?

@jgerigmeyer
Copy link
Author

@paulfalgout Ok, yeah I think that works. If I want to use reorderOnSort: true, I also need to fire the addChildView in an onReorder handler, but that's not a big deal.

Generally, I find @rafde's solution a bit cleaner, because I don't need to worry about introducing indexing bugs down the road, and I'm not using the undocumented addChildView fn.

Thanks all! I'll leave it up to the maintainers to decide whether this use-case deserves better documentation or a cleaner API.

@paulfalgout
Copy link
Member

addChildView is documented here: http://marionettejs.com/docs/master/marionette.collectionviewadvanced.html#collectionviews-addchildview

Whichever solution works for you as long as we're covering the edge cases for remove CompositeView. But note that attaching the collection view's el after the render will mean lifecycle events of the child views won't be running. the child views will not receive onAttach events nor with they be aware that they are attached.

@jgerigmeyer
Copy link
Author

jgerigmeyer commented Sep 29, 2016

For anyone following along at home, I want to note that the recommended solution above (#3150 (comment)) isn't quite as straightforward as it seems.

  1. If you want addChildView to work consistently with sorting/filtering of the collection, you need to call it both onRender and onReorder.
  2. Because of view indexing bugs in Marionette (specifically item 2 in Logic issues in CollectionView rendering with filter and renderOnSort #2948 (comment)), calling addChildView(myView, 0) will not consistently prepend myView to the beginning of the list of children. Instead, you need to find the childview with the lowest _index, and use that instead:
const minIndex = _.min(this.children.map((child) => child._index));
this.addChildView(myView, minIndex);

I would still argue that this (having additional markup outside the list of children) should be a feature/option built into a CollectionView. I'll try to think of a proposal or PR that could solve this in a nicer way.

@paulfalgout
Copy link
Member

True for 1 if using reorderOnSort.

I think for 2, that might be fixed already in the upcoming 3.1 and if not I would expect it in 3.2. @rafde is the index issue already resolved or is it an add issue? Also I have no idea what will happen off hand, but you could try this.addChildView(myView, -Infinity) in the meantime... Might be hacky to get around the real issue, but cheap and consistent.

@rafde
Copy link
Member

rafde commented Sep 29, 2016

😢 I can't resolve re-index until I properly resolve #3173

I can prioritize that work instead of the benchmark stuff, seems to be a need to resolve that PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants