attaching backbone.js views to existing elements vs. inserting el into the DOM

B Robster picture B Robster · May 25, 2012 · Viewed 43.4k times · Source

I am implementing my first actual non-tutorial Backbone app, and have 2-ish questions about an aspect of using using backbone.js that isn't settling very well with me, which relates to injecting a view's rendered el into the DOM vs. using an existing element for el. I suspect I will provide you all with a few "teachable moments" here, and appreciate the help.

Most Backbone View examples I see in the web specify tagName, id and/or className, when creating a View and thereby create an el that is unattached from the DOM. They typically look something like:

App.MyView = Backbone.View.extend({
    tagName: 'li',
    initialize: function () {
     ...
    },
    render: function () { 
        $(this.el).html(<render an html template>);
        return this;
    }
}); 

But the tutorials don't always get around to explaining how they recommend getting the rendered el into the DOM. I've seen it a few different ways. So, my first question is: where is the appropriate place to call a view's render method and insert its el into the DOM? (not neccessarily one and the same place). I've seen it done in a router, in the view's initialize or render functions, or just in a root level document ready function. ( $(function () ) . I can imagine that any of these work, but is there a right way to do it?

Second, I am starting with some HTML markup/wireframe, and converting html portions to js templates corresponding to backbone views. Rather than let the view render an unattached element and providing an anchor point in the html to stick it in, I feel like its more natural, when there is only going to be one element for a view and it won't be going away, to use an existing, emptied wrapper element (often a div or span) as the el itself. That way I don't have to worry about finding the place in the document to insert my unattached el, which would potentially end up looking like this (note the extra layering):

<div id="insert_the_el_in_here">  <!-- this is all that's in the original HTML doc -->
    <div id="the_el">  <!-- i used to be a backbone generated, unattached el but have been rendered and inserted -->
        <!-- we're finally getting to some useful stuff in here -->
    </div>
 </div>

So part of my second question is, for a basically static view, is there anything wrong with using an existing element from the page's HTML directly as my view's el? This way I know its already in the DOM, in the right place, and that calling render will immediately render the view on the page. I would acheive this by passing the already exixting element to my view's constsructor as 'el'. That way, it seems to me, i don't have to worry about sticking it into the DOM (making question 1 sort of moot), and calling render will immediately update the DOM. E.g.

<form>
   <div someOtherStuff>
   </div>
   <span id="myView">
   </span>
</form>

<script type="text/template" id = "myViewContents"> . . . </script>

<script type="application/javascript">
window.MyView = Backbone.View.extend( {
     initialize: function () {
          this.template = _.template($('#myViewContents').html());
          this.render(); 
     },
     render: function () {
          $(this.el).html(this.template());
          return this;
     }
});
$(function () {
    window.myView = new MyView({ el: $('#myView').get(0) });
});
</script>

Is this an OK way to do it for static views on the page? i.e., there is only one of these views, and it will not go away in any circumstance. Or is there a better way? I realize that there may be different ways to do things (i.e., in a router, in a parent view, on page load, etc.) based on how I am using a view, but right now I am looking at the initial page load use case.

Thanks

Answer

jcreamer898 picture jcreamer898 · May 25, 2012

There's absolutely nothing wrong with the idea of attaching a view to an existing DOM node.

You can even just put the el as a property on your view.

window.MyView = Backbone.View.extend( {
     el: '#myView',
     initialize: function () {
          this.template = _.template($('#myViewContents').html());
          this.render(); 
     },
     render: function () {
          this.$el.html(this.template()); // this.$el is a jQuery wrapped el var
          return this;
     }
});
$(function () {
    window.myView = new MyView();
});

What I recommend is, do what works... The beauty of Backbone is that it is flexible and will meet your needs.

As far as common patterns are concerned, generally I find myself having a main view to keep track of over views, then maybe a list view and individual item views.

Another common pattern as far as initialization is concerned is to have some sort of App object to manage stuff...

var App = (function ($, Backbone, global) {
    var init = function () {
        global.myView = new myView();
    };

    return {
        init: init
    };
}(jQuery, Backbone, window));

$(function () {
    App.init();
});

Like I said before though, there's really no WRONG way of doing things, just do what works. :)

Feel free to hit me up on twitter @jcreamer898 if you need any more help, also check out @derickbailey, he's kind of a BB guru.

Have fun!