Backbone.js: How can I most effectively apply my generic Mustache pattern to a View?

Backbone JS Views are very "useful" in that they always create a DOM node for you, with a custom tag, id, and class. This is very nice and handy, but I found it creates an unfortunate situation: the DOM node created by the View is a hidden template.

This became apparent to me in our current project where we use the Barbel patterns between front and ends. With Backbone, when you want a DOM that looks like this:

<section class="note">
  <textarea>
  ...
  </textarea>

  <input type="button" class="save-button" value="Save">
  <input type="button" class="cancel-button" value="Cancel">
</section>

      

you will create templates that look like this:

<textarea>
{{& content}}
</textarea>

<input type="button" class="save-button" value="Save">
<input type="button" class="cancel-button" value="Cancel">

      

But now your template is tied to the secret root template node in your layout, which will need to be duplicated on the server side. So much for DBMS encapsulation!

I don't see immediately an obvious way to solve this problem, other than using it setElement()

with the rendered template at render time, but this creates other problems with it, such as replacing the newly rendered subtree in the DOM after each render()

.

How did you solve this problem?

+3


source to share


2 answers


This is an interesting question. I've never had to deal with this problem before, but I tried several options and I think I found one that I like.

First, here's the code:

//A base view which assumes the root element of its
//template (string, DOM node or $) as its el.
var RootlessView = Backbone.View.extend({

    //override the view constructor
    constructor: function(options) {

        //template can be provided by constructor argument or
        //as a subclass prototype property
        var template = options.template || this.template;

        //create a detached DOM node out of the template HTML
        var $el = Backbone.$(template).clone()

        //set the view template to the inner html of the element
        this.template = $el.html(); 

        //set the element to the template root node and empty it
        this.el = $el.empty()[0];

        //call the superclass constructor
        Backbone.View.prototype.constructor.apply(this, arguments);
    }
});

      

Essentially you are defining a base view that expects each derived view to have a property template

, or take template

as an argument in a hash parameter setting. The template can be a string, DOM node, or jQuery / Zepto-Wrapped DOM node. The view assumes the root node of the template as its el

and overrides the property template

as content for the root node.

You would use it like a normal view:



var View = RootlessView.extend({
    template: templateHtml,
    render: function() {
        this.$el.html(Mustache.render(this.template, this.model));
        return this;
    }
}); 

      

The property is el

accessible from get-go and is not stripped or overwritten on re-rendering. The only exception to normal behavior Backbone.View

is that if you define properties or arguments id

, cssClass

or tagName

, they will be ignored because the template provides a root element.

This is not widely tested, but it seems to pass the simplest test cases. The only downside I can think of is that the template

html string is stored on every instance of the view (instead of the prototype) and wastes precious bytes of memory, but that isn't difficult either.

Below is a working demo on JSFiddle .

+3


source


The solution is to include the root node itself (in this case, your section tag) in the generic base template (no children), and then initialize the el property for the view on instantiation:

var existingElement = $('section.note')[0];
new View({el: existingElement}) 

      



Then you can start attaching the form template as usual to the el property.

+2


source







All Articles