Pseudorandom Knowledge

Trimming Handlebars with Razor

In JavaScript heavy web applications it is important to generate HTML in an organized way. This is an idea on how to accomplish this with the Handlebars template library, ASP.NET MVC and Razor views.

~/Views/Templates/Sport.cshtml
<div>
  <h2>{{name}}</h2>
  <ul>
    {{#each events}}
      <li>{{> event}}</li>
    {{/each}}
  </ul>
</div>
~/Views/Templates/Event.cshtml
<strong>{{name}}</strong>

Each Handlebars template is put in a Razor file. By using Razor files we get some support from Visual Studio for writing the HTML. This also gives us access to the whole Razor engine, which could be used to handle localization among other things.

[OutputCache(Duration=604800)]
public ActionResult Compiled()
{
    string path = "~/Views/Templates/";
 
    var files = Directory.EnumerateFiles(Server.MapPath(path));
    var names = files.Select(f =>
        Path.GetFileNameWithoutExtension(f).ToLowerInvariant());
 
    var engine = new ScriptEngine();
    engine.ExecuteFile(Server.MapPath("~/Scripts/handlebars.js"));
    engine.Execute(@"var precompile = Handlebars.precompile;");
 
    var compiled = new StringBuilder(10240);
    compiled.Append("var templates = {");
    foreach (string name in names)
    {
        string file = path + name + ".cshtml";
        string template = RenderRazorViewToString(file);
 
        compiled.Append(name);
        compiled.Append(": Handlebars.template(");
        compiled.Append(engine.CallGlobalFunction("precompile", template));
        compiled.Append("),");
    }
    compiled.Append("};");
 
    foreach (string name in names)
    {
        compiled.AppendFormat(
            @"Handlebars.registerPartial(""{0}"", templates.{0});",
            name);
    }
 
    return Content(compiled.ToString(), "text/javascript");
}

Handlebars templates must be compiled before use. To increase performance we can compile them on the server, combine them and cache the result. To run Handlebars on the server we use Jurassic, an implementation of JavaScript for .NET. To get the templates from the Razor files see this implementation of RenderRazorViewToString.

The second foreach loop registers every template as a partial in order to use it from another template with the {{> name}} syntax. Only the templates used from other templates need to be registered but this just registers them all for simplicity.

<script src="~/Scripts/handlebars.runtime.js"></script>
<script src="~/Templates/Compiled"></script>

Since the templates are already compiled we only need the runtime version of Handlebars on the client. The precompiled templates are included like any other script.

(function () {
    var skiing = {
        name: 'Alpine',
        events: [
            { name: 'Downhill' },
            { name: 'Slalom' },
            { name: 'Super G' }
        ]
    };
    var body = document.getElementById('body');
    body.innerHTML = templates.sport(skiing);
})();

Using the templates is easy. Combine this system with a JavaScript framework and a REST resource and it could be something.