Inline Prototype Templates

If you've ever used Prototype's Templates, you'll know how powerful they are. If you haven't tried them, you should. One thing that makes them even more powerful is creating them from in-your-page HTML, rather than as JS Strings. This lets your designer build templates in raw HTML, and then you can transparently suck them into Template objects for use in dynamic rendering. At it's simplest:

<div id="myTemplate"><h1>#{text}</h1></div>
<script type="text/javascript">
  t = new Template($("myTemplate").innerHTML);
</script>

There are a couple gotchas with this technique, however.

The first is that your markup must be valid (X)HTML in the context of your document itself. So if you need a template for a table row, you have to put the markup for that template inside a "throwaway" table. If you can't package your template as valid markup, then you need to supply it into the DOM as the value of a TEXTAREA element, and extract it from the TEXTAREA element's value (using $F()) . This is a slightly inferior solution because you're not right in the DOM at that point, but only marginally.

When can't you package a template as valid markup? Here's a simplified example from an app I was working on today:

<textarea id="template_tableSkeleton">
<table>
<thead>
  <tr>#{headers}</tr>
</thead>
<tbody>
  #{rows}
</tbody>
</table>
</textarea>

The problem here is that you can't put content (the substitutions) directly in TR or TBODY elements. If you put that markup directly in the page, you'll end up with the substitutions in front of the opening TABLE tag, which is definitely not what you want. Using the TEXTAREA makes the browser treat it as raw text, so you don't get the rendering-based flip-flop.

The second one is a bit more subtle, and has to do with links. Take a look at this example:

<div id="myTemplate"><a href="#{href}">#{text}</a></div>
<script type="text/javascript">
  t = new Template($("myTemplate").innerHTML);
</script>

The problem is that the 'href' attribute of an A element in the DOM is url-escaped as part of parsing.  At least that's what the symptoms seem to indicate.  The template still contains the '#{text}' substitution, but it no longer contains the '#{href}' substitution, instead it contains the string '#%7Bhref%7D' (i.e. the braces were url-escaped). You could get around it by using the TEXTAREA trick, but you can solve it more directly by doing a 'replace' on the template string:

<div id="myTemplate"><a href="#{href}">#{text}</a></div>
<script type="text/javascript">
  t = new Template($("myTemplate").innerHTML.replace(/#%7B([a-zA-Z0-9_-]+)%7D/gi, "#{$1}"));
</script>

That will unescape any url-escaped substitutions in the template, allowing it to again render correctly.

These couple tricks apply to whatever templating toolkit you're using, because they're outside the templating engine itself.  Prototype's is the one that I'm most familiar with, but there are others.  TrimPath is one example with a far richer templating language (conditionals, loops, macros, inline script, etc.).  Overkill in many cases, especially if you're already using Prototype, but very rich if you need the flexibility.

3 responses to “Inline Prototype Templates”

  1. Claude

    As an alternative you may want to take a look at Ext.Template

  2. giles

    Thanks for the article, raised some pretty interesting and useful points.

    I am trying to add an id to an element, and making it unique by adding an id I am getting elsewhere on the page:

    // create the template
    var myTemplate= new Template(");

    // add the new mark up to the appropriate location
    new Insertion.Bottom($('myContainerDiv'), myTemplate.evaluate(myItem));

    Unfortunately prototype adds a space before and after the item id, so I get:

    Does anyone happen to know if there are any ways to prevent prototype adding the extra apaces?

    Thanks