Lorraine Bernard
Lorraine Bernard

Reputation: 13400

templateHelpers in Marionette.CompositeView

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?

Upvotes: 2

Views: 7452

Answers (4)

Lux.Capacitor
Lux.Capacitor

Reputation: 346

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 <%= functionName %> into the template where you are trying to get the number to show up visually on the front end page (since you want .length I see), marionette will simply do the work for you.

So like this:

--Template File--
<div id="followerCount"> <%= showCount %> </div>


--Helper Function in View--
templateHelpers: {
    showCount: function(){
                   return this.collection.length;
    } 
}

Hope that made sense or at least helps someone else perhaps looking for a simpler way to integrate database returned json to their templates.

Upvotes: 0

Will
Will

Reputation: 7017

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).

Upvotes: 0

Tony Abou-Assaleh
Tony Abou-Assaleh

Reputation: 3040

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.

Upvotes: 1

Claudiu Hojda
Claudiu Hojda

Reputation: 1051

Reading the Marionette Documentation serializeData method, which is the one using mixinTemplateHelpers, is only called on Item View.render method here and in your current code you do not render at all

UPDATE: This way everytime the collection receives new data it will update your view the new length

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

Upvotes: 3

Related Questions