templateHelpers in Marionette.CompositeView

Question!

I have no idea why this code is not working.
Reading the documentation,
the templateHelpers should be called.

My goal is to pass the this.collection.length to the template.

Any hints? thanks.

I am using Backbone.Marionette v0.9.5


return Marionette.CompositeView.extend({
    className: 'user-board',

    template: usersTemplate,

    itemView: userItemView,

    initialize: function () {
        this.collection = new UseList();
        this.collection.fetch();
    },

    appendHtml: function (collectionView, itemView) {
        collectionView.$el.find('ul.users-list').append(itemView.el);
    },

    templateHelpers: function () {
        console.log(this.collection.length);
    },

    serializeData: function () {
        return {
            weekly: this.options.weekly,
            users_length: this.collection.length // here the length is zero
                                           // after the fetch the length is > 0
                                           // but in template remains 0
        };
    }
});

To fix my issue I have to make the following...

    initialize: function () {
        _.bindAll(this, 'render');
        this.collection = new NewCollection();
        this.collection.fetch({
            success: this.render
        });
    }

Is there a better way to make it working?



Answers

At least in Marionette v1.0.3, I'm liking the pattern that rendering is handled automatically during a call to Region.show(), so I call that from a controller object which has the collection and passes it to the view on instantiation then shows the view. I don't even have to put this logic in a fetch success callback or explicitly bind to the 'reset' event, because the Marionette composite/collection view already knows to (re-)render itself on fetch success (which a debugger will show you).

By : Will


After using a setup like has been detailed, you can also use template helpers a bit more usefully than has been described so far.

For example,

If you simply drop in



This code only declares the a view. Can you share the code the instantiates the view and displays it? templateHelpers will be called and the data passed to the template when the template is rendered. That is, you either need to show the view in a region which implicitly calls the render method on the view, or explicitly call the render method.

To be useful, templateHelpers should return an object. For instance:

templateHelpers: function() {
    return {colLength: this.collection.length};
}

One important thing to keep in mind: fetch trigger an AJAX request that is done asynchronously. If you want to wait for the fetch to succeed before rendering the view, then you need to use Marionette.Async.


Update based on the update question

To avoid calling render from the view's initialize and only do it when render is called externally, change your code to:

return Marionette.CompositeView.extend({
    className: 'user-board',

    template: usersTemplate,

    itemView: userItemView,

    initialize: function () {
        this.collection = new UseList();
        var that = this;
        this.defer = $.Deferred();
        this.collection.fetch({
            success: that.defer.resolve,
            error: that.defer.resolve
        });
    },

    appendHtml: function (collectionView, itemView) {
        collectionView.$el.find('ul.users-list').append(itemView.el);
    },

    templateHelpers: function () {
        console.log(this.collection.length);
        // For greater flexibility and maintainability, don't override `serializeData`.
        return {
            weekly: this.options.weekly,
            users_length: this.collection.length
        };
    },

    render: function() {
        var that = this,
            args = arguments;
        $.when(this.defer).done(function() {
            Marionette.CompositeView.prototype.apply(that, args);
        });
    }
}); 

I'm resolving this.render both on success and error, otherwise if there is an error the view will never render (unless that's what you want).

Note that if you use Marionette.Async then you would return this.defer in the view's beforeRender and Marionette.Async would take care of delaying the rendering.

Also note that once this.defer is resolved, future renders will run when called as there is nothing to wait for, until this.defer has been reset programmatically.



This video can help you solving your question :)
By: admin