Custom Global Eco/JST helpers

CoffeescriptJavascriptJST

Working on Envelope lately, I realized the need for custom form helpers. We use Twitter Bootstrap for the form styles and layout, but the markup is just absurd. 5-6 lines of code for a single form element is a bid absurd, in my opinion.

This isn't the first time I've been irritated about the markup required for Bootstrap Forms. Coming for a big Rails background, I wrote the bootstrap_forms gem awhile back to reduce the complexity of working with Twitter Bootstrap forms in Rails.

Turns out, Ruby != Javascript err Ruby !== Javascript (see what I did there?)

Envelope runs on Spine.js and uses eco templates under the hood. We also use underscore.js. After searching the Interwebs, I found that you can arbitrarily extend underscore.js using the _.mixin function like so:

_.mixin
  bootstrap_field: (options) ->
    options.type ||= 'text'
    options.id ||= options.name.toLowerCase().replace /\./g, '_'
    options.list = ("#{k}=\"#{_.escape(v)}\"" for k,v of options)

    _.template('
    <div class="control-group">
      <label class="control-label"><%- options.label %></label>
      <div class="controls">
        <input <%= options.list.join(" ") %>
      </div>
    </div>
  ')(options: options)

This makes sense, right? We just do some magical escaping, interpolate strings, awesomeness.

Well, it doesn't work! Surprised? I'm not. As it turns out, when we invoke this function, the result is marked as dirty by eco. So eco escapes it. We get back the HTML literally (with some stuff even double-escaped):

&lt;div class=&quot;control-group&quot;&gt;Account Name
&lt;div class=&quot;controls&quot;&gt;&lt;/div&gt;
&lt;/div&gt;

That's really annoying… I dove into the eco compiler source code and found this:

if (value && value.ecoSafe) {
  return value;
} else {
  if (!(typeof value !== 'undefined' && value != null)) value = '';
  var result = new String(value);
  result.ecoSafe = true;
  return result;
}

It appears that there's this magical flag ecoSafe that determines whether something is escaped or not. So I set out, copied some code, and magic:

_.mixin
  bootstrap_field: (options) ->
    options.type ||= 'text'
    options.id ||= options.name.toLowerCase().replace /\./g, '_'
    options.list = ("#{k}=\"#{_.escape(v)}\"" for k,v of options)

    result = new String _.template('
    <div class="control-group">
      <label class="control-label"><%- options.label %></label>
      <div class="controls">
        <input <%= options.list.join(" ") %>
      </div>
    </div>
    ')(options: options)

    result.ecoSafe = true
    result

I'll be publishing these mixins as their own drop-in library + a part of my bootstrap_forms gem soon.

Seth Vargo is a Developer Advocate at Google. Previously he worked at HashiCorp, Chef Software, CustomInk, and a few Pittsburgh-based startups. He is the author of Learning Chef and is passionate about reducing inequality in technology. When he is not writing, working on open source, teaching, or speaking at conferences, Seth enjoys spending time with his friends and advising non-profits.