How to render a view from a route?

715 views Asked by At

My app contains two views with templates, router, model and collection.

root
 js
  collections
   -collection.js
  models
   -model.js
  routers
   -router.js
  templates
   -add_item.html
   -list.html
  views
    -add_item.js
    -list.js
index.html

In router.js, I'm trying to navigate to a child view.

define([
    'jquery',
    'backbone',
    'views/item'
], function($, Backbone, ItemView) {
    'use strict';

    return Backbone.Router.extend({
        routes: {
            '': 'list',
            'list/add': 'addItem',
            'list/edit/:id': 'editItem'
        },

        addItem: function() {
            new ItemView();
        }
    });
});

By looking at the call stack, I see that my router triggers. But the template of my child view doesn't initializes.

item.js code:

return Backbone.View.extend({
    template: _.template(itemTemplate),

    events: {
        'click #save': 'saveItem',
        'click #cancel': 'cancel'
    },

    initialize: function() {
        this.render();
    },

    render: function() {
        this.$el.html(this.template);
        return this;
    }
});
1

There are 1 answers

7
Emile Bergeron On BEST ANSWER

Underscore's _.template

Underscore _.template function takes a template string as argument (and optionally a settings object) and returns a new pre-compiled template function which can take an object as an argument.

template: _.template(itemTemplate), // this returns a function, which is fine here.

This line is fine, but the following one puts the returned function as the HTML content:

this.$el.html(this.template); // a function reference as HTML?!

You need to call the pre-compiled template function to get the string:

this.$el.html(this.template());

Backbone view rendering

Now that the view's default root element (el) is correctly filled with the template content.

<div>The div element is the default root element, this text is the template</div>

But once the template is rendered, the view's element is still not in the page. To make it part of the page, you could:

  • put the view's el manually into another element in the router

    addItem: function() {
        var view = new ItemView();
        $("#content").html(view.render().el);
    }
    

    I put view.render() here because rendering in the initialize is not my preferred pattern as it's limiting the reuse of the view. But it really depends on what's the view for.

  • pass a selector string or element to the el constructor option of the view

    new ItemView({ el: '#the-view-element-id' });
    
  • create the view with a hard-coded el property (this can be overridden by the el option above)

    Backbone.View.extend({
        el: '#the-view-element-id',
        /* ... */
    

Default route

If you want to default to the list route, you could make the routes object like this:

routes: {
    'list/add': 'addItem',
    'list/edit/:id': 'editItem'
    // catch-all (default) route at the end
    '*anything': 'list',
},

Additional information: