Backbone.Marionette: Add header to collection view
Asked Answered
B

3

8

Using Backbone.Marionette, I would like to render a collection of items along with a header.

I'm aware that Marionette.CollectionView does not have a template, as it only renders ItemViews.

I'm currently using Marionette.LayoutView, but have to define an extra DOM element for the 'list' region.

Is there any other way to do this? Possibly without the extra DOM element?

Maybe I could change open() for this particular region?


Current Result:

<div class='collection'>
  <h3>Featured</h3>
  <div class="list"></div>
</div>

Desired Result:

<div class='collection'>
  <h3>List Name</h3>
  <ul>
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
   </ul>
</div>

Render Code:

var col = new LCollection([{name: "foo"}, {name: "bar"}]); // Defined earlier, not relevant here
var list = new ListView({collection: col});
var layout = new MyLayout({model: new Backbone.Model({name: "Featured"})});

app.featured.show(layout);
layout.list.show(list);

Views:

var ListItemView = Backbone.Marionette.ItemView.extend({
  template: '#list-item',
  tagName: 'li'
});

var LessonListView = Backbone.Marionette.CollectionView.extend({
  tagName: 'ul',
  itemView: ListItemView
});

var MyLayout = Backbone.Marionette.Layout.extend({
  template: "list-layout",

  regions: {
    list: '.list'
  }
});

Templates:

<script type="text/template" id="list-item">
  <%= name %>
</script>

<script type="text/template" id="list-layout">
  <h3><%= name %></h3>
  <div class="list"></div>
</script>
Barbaresi answered 1/8, 2013 at 3:15 Comment(1)
Why not a CompositeView?Carnatic
B
14

https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.compositeview.md

Applied for you :

Templates

<script id="list-item" type="text/html">
  <%= name %>
</script>

<script id="list-layout" type="text/html">
    <div class='collection'>
        <h3><%= name %></h3>
        <ul></ul>
    </div>
</script>

JS

RowView = Backbone.Marionette.ItemView.extend({
  tagName: "li",
  template: "#list-item"
});

TableView = Backbone.Marionette.CompositeView.extend({
  itemView: RowView,
  // specify a jQuery selector to put the itemView instances in to
  itemViewContainer: "ul",
  template: "#list-layout"
});
Burnie answered 1/8, 2013 at 4:2 Comment(4)
Can't believe I didn't see this before: or for scenarios where a collection needs to be rendered within a wrapper template. I'll try this out, and will let you know if it worked. Thanks!Barbaresi
These days it's "childView" and "childViewContainer" instead of "itemView" and "itemViewContainer"Unit
@CraigMyles I wish I had noticed your comment earlier - that would save me an hour or so :)Ticknor
@MikhailBatcer I don't know what it should say now. Toss an edit in there to have it up to dateBurnie
L
1

Marionette 3 doesn't use CompositeView any more, so if we want to get something like this:

<div class='collection'>
  <h3>List Name</h3>
  <ul>
    <li>FOO - BAR</li>
    <li>FOO - BAR</li>
    <li>FOO - BAR</li>
    ...
 </ul>
</div>

We have to use CollectionView & View with regions.

1 - Templates

We are going to define two templates, a first one that is the 'parent' and another one for each 'child' that the parent has.

parentTemplate.html

<div class='collection'>
    <h3>List Name</h3>
    <ul></ul>
</div>

childTemplate.html

<p>Item <%= value%></p>

2 - Backbone Model and Collection

Lets define a default model and its collection to fill the list.

const Item = Backbone.Model.extend({});
const Items = Backbone.Collection.extend({
    model: Item
});

3 - Views

First, we have to create a View for our child representation, so:

import ItemViewTemplate from './childTemplate.html';    

const ItemChildView = Marionette.View.extend({
    template: _.template(ItemViewTemplate),
    className: 'item-child-view',
    tagName: 'li' // <-- The element where we will wrap our item template (it is not defined in the HTML file)
});

Second, we have to create a CollectionView that manages each child, so:

const ItemsCollectionView = Marionette.CollectionView.extend({
    tagName: 'ul', // <-- The element where we will wrap our collection of items (it is already in the parent HTML file)
    childView: ItemChildView,
    className: 'items-collection-view'
});

Finally, we have to create the main View, that contains the parentTemplate.html and in this one we define the region where we will load the collection of elements, so:

import ItemsViewTemplate './parentTemplate.html';    

const ItemsView = Marionette.View.extend({
    tagName: 'div',
    className: 'items-view',
    template: _.template(ItemsViewTemplate),
    regions: {
        body: {
            el: 'ul', // <-- This is the HTML tag where we want to load our CollectionView
            replaceElement: true // <-- With this option, we overwrite the HTML tag with our CollectionView tag
        }
    }
});

4- Rest of the Code

Now we create the instances needed to make this works:

const app = new Marionette.Application({
    region: '#main'
});

// Three simple Items
let item1 = new Item({value: 1});
let item2 = new Item({value: 2});
let item3 = new Item({value: 3});

// Put this Items into a Collection of Items
let items = new Items([item1, item2, item3]);

// Create the main View
let itemsView = new ItemsView();

app.showView(itemsView);

// Create the CollectionView for the Items
let itemsCollectionView = new ItemsCollectionView({
    collection: items
});

// Load the CollectionView of Items into the region that we specified in the main View
itemsView.showChildView('body', itemsCollectionView);

app.start();

5- Result

If we run this, we get:

<div class="collection">
    <h3>List Name</h3>
    <ul class="items-collection-view">
        <li class="item-child-view"><p>Item1</p></li>
        <li class="item-child-view"><p>Item2</p></li>
        <li class="item-child-view"><p>Item3</p></li>
    </ul>
</div>

Hope it helps! Reference

Lopez answered 1/9, 2017 at 10:18 Comment(0)
A
0

I believe the solution above will render the li element directly under the containing div or other region specified, unless you also specify

tagName: "ul"

Then it renders the li elements within a ul element within the specified region.

Araroba answered 27/1, 2015 at 23:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.