Search code examples
javascriptjquerybackbone.js

How to define view Element within a view's method in backbone.js?


In building a view to control re-usable ui controls (tabs, modals, alerts, etc). I want to be able to call ui.tabs(options) which will then create the view 'object'.

I've managed to get so far that I can call ui.tabs(options). But now I can't quite figure out how to set the element with a views method (ie: tabs()). When I set a template to this.el, this.$el or this.$el.html I just get an undefined error.

Can someone explain where i'm going wrong?

Here's my code so far (simple I know):

/* UI Tools */
define(
    [
        "backbone",
        "text!templates/ui-tabs.html"
    ],
    function (Backbone, tabsTemplate) {
        var uiView = Backbone.View.extend({
            events: {
                "click .tab": "clickTab"
            },

            initalize: function () {

            },


            /*
             * TAB CONTROLS
             */
            tabs: function (options) {
                console.log(options);
                console.log(this.$el);

                this.el = $(_.template(tabsTemplate, options));


            },

            clickTab: function () {
                console.log('tab clicked');
            },

            /*
             * MODAL CONTROLS
             */
            modal: function () {

            },


            /*
             * ALERT CONTROLS
             */
            alert: function () {

            },


            /*
             * CORE
             */
            render: function () {
                return this.$el;
            }



        });

        return new uiView();
    }
);

Solution

  • Using Underscore's templating

    _.template returns a function. Use the returned function to render a template:

    var templateFunc = _.template(tabsTemplate, options); // returns a function
    templateFunc({ you: "data" }); // returns a string
    

    Additional information

    Changing el

    ✘ Do not set el or $el like this:

    this.el = /* ... */;
    

    ✔ Instead, use this.setElement which ensures that both el and $el are set correctly and re-delegates events:

    this.setElement(this.template());
    

    Making reusable components

    In a Backbone app I made, I made one view for each reusable components instead of one instance for all of them.

    Split the views and return the constructor to build a component when you need one.

    define([/* ... */], function(/* ... */) {
        var ModalView = Backbone.View.extend({
            /* ... */
        });
    
        return ModalView;
    });
    

    And do the same for every component.

    Then in a bigger component, like a page layout, use a lot of smaller components.

    define([
        'modal-view',
        'tabs-view'
    ], function(ModalView, TabsView) {
        var Layout = Backbone.View.extend({
            /* ... */
            initialize: function(){
                this.view = {
                    modal: new ModalView(),
                    tabs: new TabsView(),
                };
            },
        });
    
        return Layout;
    });