1
votes

I was unable to find any posts relevant to this error. I am attempting to render a Backbone Collection in a Marionette ItemView. The template is rendered, however, the data related to the collection is not rendered in the template. I am getting no errors or other indicators. For reasons I do not understand, using setTimeout() on App.mainRegion.show(overView). However, I know that that is not an acceptable solution. Could someone give me some insight on how to make an ItemView for a Collection properly render in this case? Here is my simplified code:

My Collection to be rendered:

About.Collection = Backbone.Collection.extend({
    url: '/api/about',
    idAttribute: '_id',
});

Involved View definitions:

About.ListView = Marionette.CollectionView.extend({
    tagName: 'ul',
    itemView: App.About.ListItemView,   
});

About.OverView = Marionette.ItemView.extend({
    tagName: 'div',
    className: 'inner',
    template: _.template('<h2>About Overview</h2><p><%= items %></p>'),
});

My relevant execution code:

var API = {
    getAbouts: function() {
        var abouts = new App.About.Collection();
        abouts.fetch();
    return abouts;
    },
    ...
}

var abouts = API.getAbouts();

var aboutsListView = new App.About.ListView({collection: abouts }),
aboutsOverView = new App.About.OverView({collection: abouts});

// Correctly renders collection data
App.listRegion.show(aboutsListView);

// Does not render collection data
App.mainRegion.show(aboutsOverView);

// unless
setTimeout(function() {App.mainRegion.show(aboutsOverView)}, 50);

For those who are interested, I am using an ItemView with the eventual intent to display aggregate data of About.Collection. I will be happy to provide additional information, if needed.

2

2 Answers

1
votes

It's an issue with the asynchronous nature of the fetch call on your collection. The data for the collection has not returned when you show the two views. If you update the execution part of your code something like the following (untested), you should be on the right tracks:

var API = {
    getAbouts: function() {
        // Just return the new collection here
        return new App.About.Collection();
    },
    ...
}

// Fetch the collection here and show views on success
var abouts = API.getAbouts().fetch({
    success: function() {
        var aboutsListView = new App.About.ListView({collection: abouts }),
        aboutsOverView = new App.About.OverView({collection: abouts});

        // Should render collection data now
        App.listRegion.show(aboutsListView);

        // Should render collection data now
        App.mainRegion.show(aboutsOverView);
    }
});
0
votes

The abouts.fetch call is asynchronous, and a significant amount of time elapses before the collection receives data from the server. This is the order in which things are happening:

  • You call getAbouts, which itself calls abouts.fetch to make GET call to server for collection.
  • The listRegion.show and mainRegion.show calls are made, rendering the 2 views with the empty collection (the collection hasn't received a response from the server yet).
  • The GET call eventually returns, and the collection is populated with data.
  • Only the aboutsListView re-renders to show the data (see below for the reason).

The reason that only the aboutsListView re-renders is that the Marionette CollectionView automatically listens for the collection's reset event, which is fired when the collection's contents are replaced.

You can fix this by simply adding an initialize function to your OverView, so that view also re-renders in response to the same event:

// add to About.OverView:

initialize: function() {
    this.listenTo(this.collection, 'reset', this.render);
}

That will take care of it.