Reputation: 1204
Basically, I'm trying to render a CompositeView as a simple four-column list with a table header, where each model in a collection is rendered into a and appended to the . I'm following an example of Derick's quite closely with only a little variation, but unfortunately have gotten some pretty strange results.
Instead of rendering each itemView, the view instead references itself and re-renders for each item in the collection, producing a new table and table head. Before that, It was rendering the compositeView in itself.
I have an itemView, the template of which is a group of items, and a compositeView that references it which is a table
The CompositeView:
class App.module('Views.Parts').Index extends Backbone.Marionette.CompositeView
template: 'parts/index'
itemView: App.Views.Parts.Part
tagName: 'table'
itemViewContainer: 'tbody'
appendHtml: (collectionView, itemView, index)->
collectionView.$el.append(itemView.el)
The ItemView:
class App.module('Views.Parts').Part extends Backbone.App.ItemView
tagName: 'tr'
template: 'parts/part'
events:
"click .destroy": "destroy"
destroy: (e) ->
e.preventDefault()
@model.destroy()
onRender: ->
@stickIt()
The Controller
class App.Controllers.Parts
constructor: ->
@parts = new App.Collections.Parts
@parts.reset(App.parts)
App.parts = null
showView: (view)->
App.mainRegion.show view
index: ->
view = new App.Views.Parts.Index
collection: @parts
@showView view
I have also heard that declaring an ItemView before the CompositeView is necessary--however since it is a Marionette Rails project, the views are actually living in different directories. Would I have to declare their order or bind them to each other accordingly in another way?
Upvotes: 3
Views: 1259
Reputation: 1303
TL;DR Marionette.CompositeView uses itself as the item view type if none defined. Set the itemView property on prototype to make it use the correct item view
In our case issue was that Marionette gets the ItemView of a CompositeView by the following logic:
getItemView: function(item){
var itemView = Marionette.getOption(this, "itemView") || this.constructor;
if (!itemView){
throwError("An `itemView` must be specified", "NoItemViewError");
}
return itemView;
}
We had defined the CompositeView as:
class sm.Views.GreetingPicker extends Marionette.CompositeView
template: 'greeting_picker'
el: '.message-choice'
itemView: sm.Views.WelcomeMessage
When the CompositeView, ItemView & Layout that tied them together were in one file (served by Rails using Sprockets), the file loaded all at once and there was no problem.
After I split up the Layout, CompositeView & ItemView, the CompositeView's appendHtml's both arguments were of the same type as CompositeView. This was caused by the Marionette's logic mentioned in the beginning.
In the initializer set the itemView property on constructor manually. Like this:
class sm.Views.GreetingPicker extends Marionette.CompositeView
initialize: ->
@constructor::['itemView'] = sm.Views.WelcomeMessage
Upvotes: 2
Reputation: 25994
You don't want to have itemViewContainer
AND appendHtml
. Try removing the latter and your views should render properly.
Upvotes: 2