0

I am trying to wrap my head around how to render multiple ItemViews, each with a distinct model and template, in a CollectionView. The Marionette.js docs specify that a CollectionView supports a single ItemView. Am I wrong in making this assumption or can the CollectionView support this? If not, what is recommended? Thank you in advance for your assistance.

UPDATE:

I added the following code:

privObj.propertiesSubPanelView = new Marionette.CollectionView({
                    el: options.el,
                    collection: col,
                    getItemView: function( item ) {
                        console.log( item );
                    }
                });
privObj.propertiesSubPanelView.render.done( function() {
    console.log( 'Im done' );
});

which is giving the following error:

An `itemView` must be specified

UPDATE #2:

I have implemented the getItemView function within CollectionView as follows:

var ColView = Marionette.CollectionView.extend({
    collection: col,
    itemViews: views,

    getItemView: function( item ) {
        var viewId,
        itemViewObj,
        itemView;

        viewId = item.get( 'name' );
        itemViewObj = Marionette.getOption( this, 'itemViews' );
        itemView = itemViewObj[viewId];

        if ( _.isUndefined( itemView ) ) {
            throw new Error( 'No view associated with name: ' + viewId );
        }
        return itemView;
    }
});

var colView = new ColView();

var propLayout = new PropLayout();
propLayout.properties.show( colView );

However, I not get the following error (Uncaught TypeError: object is not a function) in:

Marionette.CollectionView.buildItemView: function(item, ItemViewType, itemViewOptions){
    var options = _.extend({model: item}, itemViewOptions);
    return new ItemViewType(options); <<== this line!
}

Did I miss something or is this a bug?

UPDATE #3

Here is my principle function...

           newPropertiesSubPanelCollection: function( col, views ) {
                var labelModel1 = new Backbone.Model({
                    name: 'Properties',
                    value: 'Properties',
                    data: undefined
                });
                var labelView1 = new Label_.Item();

                var labelModel2 = new Backbone.Model({
                    name: 'Configure',
                    value: 'Properties',
                    data: undefined
                });
                var labelView2 = new Label_.Item();

                var col = new Backbone.Collection();
                col.add( labelModel1 );
                col.add( labelModel2 );

                var views = {};
                views['Properties'] = labelView1;
                views['Configure'] = labelView2;

                var ColView = Marionette.CollectionView.extend({
                    collection: col,
                    itemViews: views,

                    getItemView: function( item ) {
                        var viewId,
                            itemViewObj,
                            itemView;

                        viewId = item.get( 'name' );
                        itemViewObj = Marionette.getOption( this, 'itemViews' );
                        itemView = itemViewObj[viewId];

                        if ( _.isUndefined( itemView ) ) {
                            throw new Error( 'No view associated with name: ' + viewId );
                        }
                        return itemView;
                    }
                });
                var colView = new ColView();
                return this.propertiesSubPanelCollection = colView;
            },
Chris
  • 469
  • 12
  • 25

3 Answers3

1

Looking at the source of https://github.com/marionettejs/backbone.marionette/blob/master/src/marionette.collectionview.js we can see that:

getItemView: function(item){
    var itemView = Marionette.getOption(this, "itemView");

    if (!itemView){
        throwError("An `itemView` must be specified", "NoItemViewError");
    }
    return itemView;  
},  

This means that it'll look for the itemView attribute by default. However, if you override this function (as David Sulc is saying) you can do anything you want here. If you'd like, you can provide an object with views for example:

privObj.propertiesSubPanelView = Marionette.CollectionView.extend({
    el: options.el,
    itemViews: {
        view1: itemView1,
        view2: itemView2 // etc..
    }
    getItemView: function( item ) {
        // Get the view key for this item
        var viewId = item.get('viewId');

        // Get all defined views for this CompositeView
        var itemViewObject = Marionette.getOption(this, "itemViews");

        // Get correct view using given key
        var itemView = itemViewObject[viewId];


        if (!itemView){
            throwError("An `itemView` must be specified", "NoItemViewError");
        }
        return itemView;
    }
});

// Create view instance
var viewInstance = new privObj.propertiesSubPanelView({
    collection: col
});

// Your model might have the following attribute
model.get('viewId'); // returns 'view1';

There is also another error in your question, which is new Marionette.CollectionView({. You can't do that, see the example above. You need to extend the view first before invoking the new keyword on it.

Added a jsFiddle demonstrating the code above: http://jsfiddle.net/Cardiff/L8xG9/

  • I updated the answer because there was another error in your sample code. It has to do with extending the view before using it, see answer above. – Wilbert van de Ridder Apr 28 '14 at 17:57
  • Thanks Wilbert. However, this implementation generates an error as listed in UPDATE #2. Is this my bug or one existing within Marionette.js? – Chris Apr 28 '14 at 20:47
  • What are you passing exactly as your `views` variable? Looking at the marionette docs it tries to instantiate the object you returned from your `getItemView` function. If you don't supply the correct view here, it'll throw that error. – Wilbert van de Ridder Apr 28 '14 at 20:54
  • I included what constitutes my views object. Again, thanks for the persistent help! – Chris Apr 28 '14 at 21:26
  • Can you try `var labelView1 = Label_.Item;` instead of `var labelView1 = new Label_.Item();`. This is because you don't need to instantiate the view here. The collection/compositeView will do that for you. Hence the `return new ItemViewType(options);` in *Marionette.CollectionView.buildItemView*. – Wilbert van de Ridder Apr 28 '14 at 22:18
0

You can define getItemView to return a different item view according to your model: https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.collectionview.md#collectionviews-getitemview

David Sulc
  • 25,946
  • 3
  • 52
  • 54
  • Thanks David! I am implementing now and will update ticket accordingly. BTW, purchased you eBook which is excellent. – Chris Apr 21 '14 at 15:33
  • Still confused. How do I add multiple ItemViews to the CollectionView as ItemView does not accept an array or collection of ItemViews? – Chris Apr 21 '14 at 20:17
0

This was an issue with some of the old libraries of backbone and Marionette. I used the following libraries and it fixed the issue.

https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.3.2/backbone-min.js https://cdnjs.cloudflare.com/ajax/libs/backbone.marionette/2.4.5/backbone.marionette.min.js

Amit
  • 559
  • 5
  • 8