Question

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;
            },
Was it helpful?

Solution

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/

OTHER TIPS

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

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top