I've been using Handlebars in Node.js apps for a long time now and while it always gets the job done, sometimes I find it awkward to use.  In particular I miss being able to use expressions and flexible logic flow directly in the templates.  

I know there's an argument for keeping logic and views separate and that views should be "logicless", but sometimes you just want a bit more control.  The existence of libraries like Swag, suggests that I'm not alone here so I got to thinking...

What would a JavaScript enabled template language with a similar syntax to Handlerbars look like?  

In the end I came up with Moe-js: a modern template engine for JavaScript.

Full documentation for Moe-js is available here. This post explains the rationale behind it.

Background

The original motivation for Moe-js came during a re-write of my own website http://www.toptensoftware.com.  The site is mostly static content driven by a set of plain text files - basically Markdown files with a bit of YAML "front matter" at the top defining additional data associated with that page.

In this case I'm really using Express' view engine as a theming engine and being able to include a reasonable sprinkling of logic in the view meant I could completely eliminate that middle "business logic" layer.

The other thing I needed was the ability for a template to load the meta-data for referenced pages. eg: loading a referenced page to pull out its title, a synopsis summary or the URL of its banner image.  As we all know, doing this synchronously is bad form in Node so I wanted a template engine that could render templates asynchronously.

Design Goals

Bringing all this together I came up with the following design goals:

  • Handlebars like syntax
  • Embedded JavaScript support
  • Compiled and fast
  • Async enabled
  • Easy extensibility model

Handlebars Like Syntax

Despite my gripes with Handlebars I really like the syntax - it's easy to understand, easy to learn, easy to type and clearly distinguishes template script from markup.

Also, I had a lot of projects using Handlebars so I wanted a syntax that would be easy to convert to the new template engine.

If you're familiar with Handlebars, Moe-js will seem very familiar:

<h1>{{model.title}}</h1>
{{#each item in model.items}}
    {{#if item.price != 0}}
        <p>{{item.name.toUpperCase()}} - {{{helpers.formatPrice(item.price)}}}</p>
    {{/if}}
{{/each}}

Embedded JavaScript Support

Moe-js supports JavaScript expressions.  In the above example, note the following:

  • Calling a JavaScript member: item.name.toUpperCase()
  • JavaScript conditions: item.price != 0
  • Helper functions: helpers.formatPrice(...)

You can also embed entire code blocks with the code directive:

{{#code}}
function doSomething(value)
{
    ...
}
{{/code}}

{{doSomething(99)}}

Compiled and Fast

For simplicity and performance, Moe-js compiles templates to straight JavaScript.

In order to avoid dynamic lookups at runtime, Moe-js requires all data properties be explicitly referenced.  To explain, in Handlebars, you can reference a data property just by name:

<p>{{title}}</p>

Because of the embedded JavaScript support, this won't work in Moe-js. Without completely parsing and understanding the expression and without knowing the properties of the data object eventually passed to the template, there's no way to know where that "title" property comes from.  It could be a property on the passed data object, a local variable, a scope variable etc...

For this reason, data properties must be explicitly referenced via the special model property:

<p>{{model.title}}</p>

Similarly, in loop iterators, a variable is used to explicitly specify which loop variable to use:

{{#each u in model.Users}}
{{#each r in u.roles}}
<p>Name: {{u.name}} Role: {{r}}</p>
{{/each}}
{{/each}}

While this makes conversion of templates from Handlebars to Moe-js more tedious than I'd like, in the end it makes the compilation simpler, rendering faster and I've come to prefer this more explicit style.

Async Enabled

Moe-js templates can be compiled to execute either synchronously or asynchronously.  An asynchronously compiled template can use the JavaScript await keyword:

<p>Data: {{await model.callSomeAsyncMethod()}}</p>

If you're using Moe-js' Express integration, async support is automatically enabled.

Easy Extensibility

Moe-js provides extensibility via the moe.helpers property.  Say you wanted a helper function to format prices.  Add the helper like so:

moe.helpers.FormatPrice = function (val)
{
    if (val == 0)
        return "-";
    else
        return "$" + val.toFixed(2);
}

And then call it in your template:

<p>Price: {{helpers.FormatPrice(item.price)}}</p>

Other Features

Moe-js has the typical template engine features, including:

  • Support for Partials and Layouts
  • Express View Engine Integration
  • Redirecting Template Output to a variable
  • Compiled Template Caching
  • No Dependencies

Check It Out

Because of its embedded JavaScript support, Moe-js shouldn't be used for template scripts from untrusted sources.  ie: you shouldn't let users of your site upload templates for execution on your server.

However, if you're after a simple, easy to use, modern, async enabled view engine for your next Node/Express project it might be a good option and I encourage you to give it a go and let me know what you think.

InheritDoc.io

With each post I like to share someone else's project and recently I came across InheritDoc.io by FireShark Studios.  

If you're writing C# XML documentation, you may be familiar with the <inheritdoc /> directive that is used by some documentation generators to inherit the documentation from a base class.

The trouble with this approach is those inherited declarations aren't available in the Visual Studio Intellisense.  FireShark's project resolves this by rewriting the original .XML documentation files, replacing the <inheritdoc /> directive with the actual inherited documentation.

What's more its been extended to allow pulling documentation from other methods and there's a nice site explaining how it all works.  

Check it out here: https://www.inheritdoc.io