Question

Why won't my Marionette CollectionView work with an empty collection?

I'm trying to render a CollectionView within a CompositeView (it's a list of tasks within each item of a list of projects). Everything works fine until I pass an empty collection into the CollectionView. The collection is allowed to be empty; a project can easily have no tasks assigned to it.

The error shown by Chrome is

Uncaught TypeError: Object [object Object] has no method 'on'

The collection of projects passed in is a valid Backbone.Collection, as are the task lists returned by the filter methods of TasksCollection, which is why I can't work out why the error above is shown.

Marionette's CollectionView can accept an empty collection (it has an emptyView property), so why is it giving an error when one is passed in? I assume it's trying to bind events to the collection which should "just work" - it's only the models that are absent.

My tasks collection looks like this:

var TasksCollection = Backbone.Collection.extend({
    model: TaskModel,
    byProject: function(projectId) {
        var matches = this.filter(function(task) {
            return task.get('projectId') == projectId;
        }, this);

        return new TasksCollection (matches);
    },
    complete: function(state) {
        return new TasksCollection (this.where({ complete: state }));
    }
});

My views look like this:

// Single task item
var TasksListView = Marionette.CollectionView.extend({
    tagName: 'ul',
    className: 'tasks nav nav-stacked nav-pills',
    itemView: Minuteboy.views.TaskItem,
    emptyView: Templates['partials/tasks-empty']
});

// Project item (should probably be a Layout or CompositeView)
var DashboardProject = Marionette.ItemView.extend({
    tagName: 'article',
    template: Templates['partials/dashboard-project'],
    initialize: function() {
        this.on('render', function() {
            var tasks = AllProjectTasks.byProject(this.model.get('id')).complete(false);

            this.$el.find('.tasks-wrapper').html(new TasksListView({ 
                collection: tasks
            }).render().el);
        });
    }
});

// Containing view with page title and container for projects list
var DashboardPage = Marionette.CompositeView.extend({
    template: Templates['pages/dashboard'],
    itemView: DashboardProject,
    itemViewContainer: '#content'
});

Finally, I instantiate the page view like this:

var page = new DashboardPage({
    collection: // A valid Backbone.Collection
});
Was it helpful?

Solution

This was an error on my part. Instead of using some sort of view object for the emptyView property of my CollectionView, I was just using a template (Handlebars in this case). My working CollectionView looks like this:

var EmptyList = Marionette.ItemView.extend({
    tagName: 'li',
    template: Templates['partials/tasks-empty']
});

Minuteboy.views.TaskList = Marionette.CollectionView.extend({
    tagName: 'ul',
    className: 'tasks nav nav-stacked nav-pills',
    itemView: Minuteboy.views.TaskItem,
    emptyView: EmptyList
});

Note the itemView line:

itemView: Minuteboy.views.TaskItem

This used to be

itemView: Templates['partials/tasks-empty']

Which is bad.

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