Search code examples
backbone.jsunderscore.js-templating

How to data-bind select elements with Backbone + Underscore templates?


Let's say I want to render a form using Underscore templates. Following the tutorial, I put in my Backbone view something like:

var CatFormView = Backbone.View.extend({
    render: function () { this.$el.html(this.template(this.model.toJSON())); }
)};

and in my template, things like

<label for="cat-name">Cat name:</label>
<input id="cat-name" value="<%- catName %>" />

This is all well and good, but what if my form contains a select element? Do I do something like:

<select id="cat-breed">
   <option value="siamese" <% if (catBreed === "siamese") { %> selected <% } %>>Siamese</option>
   <option value="persian" <% if (catBreed === "persian") { %> selected <% } %>>Persian</option>
   <option value="scottish-fold" <% if (catBreed === "scottish-fold") { %> selected <% } %>>Scottish Fold</option>
   ...
</select>

or do I bind the input elements directly in the template, and then set the value for the selects in the view?

The first approach looks extremely cumbersome, the second spreads the data-binding logic over several files, making the web app that much less maintainable.

What would be a better solution here?

Edit: Loops! Now it seems so obvious, but I was still stuck on thinking of Underscore templates as markup. All thanks go to Clémentine.

<% var _catBreeds = [['siamese', 'Siamese'], ['persian', 'Persian'], ...]; %>
<select id="cat-breed">
   <% for (var i = 0; i < _catBreeds.length; i++) { %>
   <option value="<%= _catBreeds[i][0] %>" <% if (catBreed === _catBreeds[i][0]) { %> 
           selected <% } %> >
       <%- _catBreeds[i][1] %>
   </option>
   <% } %>
</select>

Edit 2: Or, even better (and reusable):

<% function options(vals, selectedVal) {
  // Can be moved into a helper library and injected when compiling template
  vals.forEach(function (val, i) {%>
    <option value="<%- val[0] %>" <%= (val[0] == selectedVal) ? "selected" : "" %>><%- val[1] %></option> <%
  });
} %>
<select id="cat-breed">
  <% options([["persian", "Persian"], ...], catBreed) %>
</select>

Solution

  • another option is to use another model for your form such as

    [
    {
    'id': 'cat-breed',
    'type': 'select',
    'values': ["siamese", "scottish-fold", "persian"]
    }
    ]
    

    and then call

    this.template(data_model: this.model.toJSON(), form_model:this.form_model.toJSON())
    

    and then to build the form and with the form answers using loops.

    this would allow you to have a more generic templating system, less cumbersome, but more difficult to code.