2

A fellow front-end dev posed to me this inquiry, and I wasn't exactly sure how to articulate my stance, but he got me thinking:

So, the convention in Backbone (or Backbone.Marionette) is to render a view, which is associated with a template, and then dump or append the resultant view's constructed/composited HTML into an existing container, as such:

this.$el.append(this.subview.render().$el);

However, this, of course, requires a DOM element on the page, which has already been rendered, to dump this newly-rendered view into. This results in phenomenons like the following:

<ul id="scheduler-loadroutegroups" class="full-height"></ul>

or

collectionView.$("#scheduler-loadroutegroups").append(itemView.el);

That is, empty containers which eventually expect content.

Obviously, this render-and-append pattern is a core concept of Backbone, and having containers to place content into seems to be absolutely essential.

My coworker's question was whether it would be possible to utilize a more server-side MVC template pattern, in which you wouldn't need ANY empty containers:

<ul id="scheduler-loadroutegroups" class="full-height">
    {{#developers}}
        <ul>
            {{#pets}}
                <li>{{name}}</li>
            {{/pets}}
        </ul>
    {{/developers}}
</ul>

In this instance, say developers represent a collection of models, and pets represent a collection of models. They each would need their own view/subview, with events attached to them.

I'm under the assumption that the issue with a more classical server-side MVC templating system is that the views and their related templates would then be missing the fine-grained control over context limiting and event binding that Backbone relies on.

All Backbone examples/documentation I've seen use this "empty container/append" pattern. Is it possible to do things any other way? Why or why not?

J. Ky Marsh
  • 2,465
  • 3
  • 26
  • 32
  • 1
    At some point it's going to need to be attached to the DOM, but you don't *need* to do that in the render method (keep in mind that your `el` does not need to be an existing element). – Jack Feb 01 '13 at 17:45
  • I guess we'd need to clarify what "attached to the DOM" means. If the entire template were pre-rendered (in the sense that there were no empty containers), I guess you could then attach the view to the particular portion of the DOM using .setElement()? – J. Ky Marsh Feb 01 '13 at 17:52
  • Not exactly, [setEelement](http://documentcloud.github.com/backbone/#View-setElement) will change the view's `el` to the "new element" (re-delegating the events hash), but you could at any point insert it into some element on the DOM (including just attaching it to the body (which you probably wouldn't want to do)). – Jack Feb 01 '13 at 17:56
  • But if all of the template logic is already in the (parent) template, which has presumably already been rendered and dumped into the DOM, the "insert into the DOM" step is bypassed for all children (such as "developers" and "pets" in my example), is it not? – J. Ky Marsh Feb 01 '13 at 18:03
  • I'm not sure I get what exactly you mean, you can render the parent template completely (including all the children) and then just dump it afterwards in the DOM, any future rendered children can then then just be inserted into the parent's element which is at that point in the DOM already. – Jack Feb 01 '13 at 18:45
  • Right, I'm wondering in the instance when the parent and ALL children are rendered at once, I.E. there are no "future rendered children." The parent template, containing all child templates, is rendered and added to the DOM, and then setElement() would be called to attach a View to the child template (which is already in the DOM) after-the-fact. – J. Ky Marsh Feb 01 '13 at 20:25

1 Answers1

2

A Backbone view requires a DOM element and goes as far as providing attributes to ease its creation but nothing forces you to use a template engine or DOM manipulations. You can use an existing markup structure, you just have to attach the views to the correct DOM nodes.

Assuming a rendered HTML looking like this

<ul id='scheduler-loadroutegroups'>
    <li id='d1'>
    <ul>
        <li id='p1'>P1</li>
        <li id='p2'>P2</li>
    </ul>
    </li>
    <li id='d2'>
    <ul>
        <li id='p3'>P3</li>
        <li id='p4'>P4</li>
    </ul>
    </li>
</ul>

you could use a view that grabs the relevant DOM nodes and associate the subviews. The subviews themselves set up their DOM nodes and so on.

var DevListView = Backbone.View.extend({
    initialize: function() {
        this.inject();
    },

    inject: function() {
        var $el = this.$el;

        this.collection.each(function (model) {
            var subview = new DevView({
                el: $el.find("#d" + model.id),
                model: model
            });
        });
    }
});

var DevView = Backbone.View.extend({
    initialize: function() {
        this.inject();
    },

    inject: function() {
        var $el = this.$el;

        this.model.pets.each(function (model) {
            var subview = new PetView({
                el: $el.find("#p" + model.id),
                model: model
            });
        });
    }
});

var PetView = Backbone.View.extend({
    events: {
        'click ': function () {
            this.$el.toggleClass('selected');

            var msg = '<p>Pet '+this.model.get('id')+'</p>';
            $('body').append(msg);
        }
    }
});

You then call the root view with the relevant data and the initialize methods do the rest.

var Developer = Backbone.Model.extend({
    initialize: function () {
        this.pets = new Backbone.Collection(this.get('pets'));
    },
    parse: function (data) {
        this.pets.reset(data.pets);
        return data;
    }
});
var Developers = Backbone.Collection.extend({
    model: Developer
});
var v = new DevListView({
    el: '#scheduler-loadroutegroups',
    collection: devs
});

See http://jsfiddle.net/3xNHj/ for a demo.

Of course, you could go further and rerender your views, these questions might help you dig further

Backbone, not "this.el" wrapping
BackboneJS Rendering Problems

Community
  • 1
  • 1
nikoshr
  • 32,926
  • 33
  • 91
  • 105
  • Very interesting, thanks a lot. Out of curiosity, do you see any benefits or downfalls to attaching Views to an existing DOM structure, versus creating the structure/Views entirely on-the-fly? – J. Ky Marsh Feb 04 '13 at 23:36
  • @J.KyMarsh Progressive enhancement, SEO and app bootstrapping would go in the benefits column, dual client/server rendering would go in the drawbacks. And the direct manipulation of the DOM is probably easier to understand or maintain. – nikoshr Feb 05 '13 at 07:38