sehummel
sehummel

Reputation: 5568

Backbone view doesn't render template passed to it

I need to be able to pass different template IDs to different routes.

(function() {
    window.App = {
        Models: {},
        Collections: {},
        Views: {},
        Router: {}
    };

    var vent = _.extend({}, Backbone.Events);

    _.templateSettings.interpolate = /\[\[(.+?)\]\]/g;

    App.Router = Backbone.Router.extend({
        routes: {
            '' : 'index',
            'send-message' : 'sendMessage',
            '*other' : 'other'
        },
        index: function() {
            t = new (App.Collections.Tables.extend({ url: 'main-contact'}))();
            tables = App.Views.Tables({ collection: t, template: 'mainContactTemplate' });
            $('#web-leads').html(tables.el);
        },
        sendMessage: function() {
            // t = new (App.Collections.Tables.extend({ url: 'send-message'}))();
            // tables = new App.Views.Tables.extend({ collection: t, template: template('sendMessageTemplate')});
            // $('#web-leads').html(tables.el);
        },
        other: function() {

        }
    });

    // Main Contact
    App.Models.Table = Backbone.Model.extend({});

    App.Collections.Tables = Backbone.Collection.extend({
        model: App.Models.Table,
        initialize: function(models, options) {
            this.fetch({
                success: function(data) {
                    //console.log(data.models);
                }
            });
            if (options) {
                this.url = this.url || options.url;
            }
        }
    });

    App.Views.Tables = Backbone.View.extend({
        tagName: 'ul',
        initialize: function() {
            this.collection.on('reset', this.render, this);
        },
        render: function() {
            return this.collection.each(this.addOne, this);
        },
        addOne: function(model) {
            var t = new App.Views.Table({ model: model});
            this.$el.append(t.render().el);
            return this;
        }
    });

    App.Views.Table = Backbone.View.extend({
        tagName: 'li',
        initialize: function(options) {
            this.template = options.template;
            console.log(this.options);
        },
        retrieveTemplate: function(model) {
            return _.template($('#' + this.template).html(), model);
        },
        render: function() {
            this.$el.html(this.retrieveTemplate(this.model.toJSON()));
            return this;
        }
    });

    new App.Router();
    Backbone.history.start();
})();

But I get an error than n is undefined. I think I need to pass this.template into my retrieveTemplate function. But shouldn't it already be set? This code works, by the way, if I hard code in the name of the template ID in the retrieveTemplate function.

EDIT: the template isn't being passed from the call in the router. That's where this is breaking down.

EDIT: I took out the call to extend in the second line of the index route and now I get this._configure is not a function

WORKING VERSION:

(function() {
    window.App = {
        Models: {},
        Collections: {},
        Views: {},
        Router: {}
    };

    var vent = _.extend({}, Backbone.Events);

    _.templateSettings.interpolate = /\[\[(.+?)\]\]/g;

    App.Router = Backbone.Router.extend({
        routes: {
            '' : 'index',
            'send-message' : 'sendMessage',
            '*other' : 'other'
        },
        index: function() {
            var t = new (App.Collections.Tables.extend({ url: 'main-contact'}))();
            var tables = new (App.Views.Tables.extend({ collection: t, options: {template: 'mainContactTemplate' }}))();
            $('#web-leads').html(tables.render().el);
        },
        sendMessage: function() {
            // t = new (App.Collections.Tables.extend({ url: 'send-message'}))();
            // tables = new App.Views.Tables.extend({ collection: t, template: template('sendMessageTemplate')});
            // $('#web-leads').html(tables.el);
        },
        other: function() {

        }
    });

    // Main Contact
    App.Models.Table = Backbone.Model.extend({});

    App.Collections.Tables = Backbone.Collection.extend({
        model: App.Models.Table,
        initialize: function(models, options) {
            this.fetch({
                success: function(data) {
                    //console.log(data.models);
                }
            });
            if (options) {
                this.url = this.url || options.url;
            }
        }
    });

    App.Views.Tables = Backbone.View.extend({
        tagName: 'ul',
        initialize: function(options) {
            this.collection.on('reset', this.render, this);
            this.template = this.options.template;
        },
        render: function() {
            this.collection.each(this.addOne, this);
            return this;
        },
        addOne: function(model, options) {
            //console.log(model);
            var t = new App.Views.Table({ model: model, template: this.options.template});
            this.$el.append(t.render().el);
            return this;
        }
    });

    App.Views.Table = Backbone.View.extend({
        tagName: 'li',
        initialize: function(options) {
            //console.log(this.options);
            this.template = this.options.template;
        },
        retrieveTemplate: function(model) {
            return _.template($('#' + this.template).html(), model);
        },
        render: function() {
            //console.log(this);
            this.$el.html(this.retrieveTemplate(this.model.toJSON()));
            return this;
        }
    });

    new App.Router();
    Backbone.history.start();
})();

Upvotes: 2

Views: 765

Answers (2)

mu is too short
mu is too short

Reputation: 434685

Your router says this:

tables = App.Views.Tables({ collection: t, template: 'mainContactTemplate' });

So you're giving a template: '...' to App.Views.Tables. The initialize in App.Views.Tables looks like this:

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

so it ignores the template option. If we look at App.Views.Table (singular!), we see this:

initialize: function(options) {
    this.template = options.template;
    console.log(this.options);
}

but App.Views.Table is instantiated without a template option:

var t = new App.Views.Table({ model: model});

You need to fix how you use App.Views.Table. Backbone will put a view's constructor options in this.options for you so you just need to say:

var t = new App.Views.Table({ model: model, template: this.options.template });

A couple other things to consider:

  1. You have some accidental globals in your router's index method, you should have var t and var tables rather than just t and tables.
  2. A view's render method conventionally returns this so that you can say $x.append(v.render().el) so you might want to adjust your render methods to match the convention.

Upvotes: 1

Jeff Procsal
Jeff Procsal

Reputation: 11

You probably need to bind the context. Underscore can help you with that.

.bindAll or .bind should do it.

I typically just use _.bindAll during initialization as shown below.

...
initialize: function(options) {
    _.bindAll(this); // apply appropriate context
    this.template = options.template;
},
...

Hope this helped, best of luck.

Upvotes: 1

Related Questions