backbone: Don't wrap views if using templates

Problem

Most of my render methods end with something like:

var template = _.template( $("#asset-template").html(), data);
$(this.el).html(template);

With a template like:

<script id="asset-template" type="text/template">
  <li class="asset {{ classes }}">
    <img src="{{ img_url }}" data-url="{{ url }}" data-description="{{ description }}">
    <span class="caption">{{ description }}</span>
  </li>
</script>

So the template gets inserted into this.el (<div> by default).

But I tend to write my templates ‘fully-formed’ (as above), not requiring any extra wrapper elements. This presents 2 problems:

  • Your <li> is wrapped in a div (producing invalid markup) or another unneeded element
  • Your events are more verbose: 'click .asset' : 'select' instead of just 'click' : 'select'

One solution would be to exclude the <li> from the template, and have Backbone construct it with the el, className, etc. properties. But a big benefit of using templates is that they are not mixed with the view logic, and that you can write them just like normal HTML.

Solution(s)

My suggestion would be a new template property:

App.Views.Asset = Backbone.View.extend({

  events: {
    'click' : 'select'
  },

  template: _.template($("asset-template").html()),

    ...

}

If template is present, it’s presumed the template is fully-formed and no wrapper elements are created.

Another option would be the ability to assign the template directly to this.el, while keeping the event delegation working:

// this currently works, but you lose event delegation
this.el = _.template( $("#asset-template").html(), data);

Could be I’m missing something and it’s possible to achieve ‘unwrapped templates’ now, but after using Backbone for a couple of projects I kept stumbling on this.

About this issue

  • Original URL
  • State: closed
  • Created 13 years ago
  • Comments: 28 (4 by maintainers)

Most upvoted comments

I support this feature in theory, but the issue comes down to operations that depend on the this.el. The usage of the template and the context/data that is passed to the template can be quite diverse across implementations. My current solution is to override this.el in the render method:

render: function() {
    // unbind all handlers since delegateEvents was called in the constructor
    $(this.el).unbind();
    this.el = this.template(this.model.toJSON());
    this.delegateEvents();
}

One possible solution to facilitate use of templates (and prevent redundant operations), is to check if this.template is defined during initialization. It is is, this.el would not be created and any operations that immediately depends on it (delegateEvents) would not occur.