A commonly performed task is the transformation of a piece of text and some data into another piece of text. This process is usually achieved with the help of a templating language.

There are many choices available for templating, and all have different properties that make them more or less useful for your purpose. There is, thus, a number of features that you should consider when choosing a templating language for your application, such as:

  • Compilation performance
  • Size of runtime
  • Input / output format
  • No logic / some logic / a lot of logic
  • Verbosity and overhead of templates
  • Asynchronous / synchronous
  • Fit for server and/or client
  • Ease of use

 

At Adverai we have built an intelligent event stream that learns from your data and gives you immediate recommendations about the appropriate correcting actions for your ads, campaigns, and marketing strategies.  We needed a templating language that could take data about an event, together with a template for the type of event, and generate a piece of HTML to support formatting of the text.

We already use AngularJS, the JavaScript-based open-source front-end web application framework maintained by Google, in our application.  Angular comes with the ability to compile templates, but we needed something that had less overhead when creating and editing the templates for the events, and that didn’t require an understanding of Angular itself to use.

However, what we did need was support for at least some logic in order to perform actions such as resolving the ids of entities to the name of an entity. This eliminated some of the simpler alternatives. The options available to us in the community where we could implement the necessary features were an overkill for this use case in particular. So we decided to put a simple alternative together.

Continuous Template Compilation

We already make use of RxJS and Observables in our application, so an early decision we made was to make a templating runtime based on these.  This decision introduced some interesting benefits such as continuous template compilation, and the result is a very small core templating runtime that is less than 2KB gzipped, with a small subset of RxJS being the only dependency.

The utility ended up being called whiskers and is initially focused on providing HTML output which is apparent in the syntax, but could be used for any kind of output. There are three main concepts to whiskers: quasi-elements, interpolation and transforms.

Using whiskers is very simple, especially if you are comfortable with observables.

const whiskers = new Whiskers();
whiskers.compile('This is my template')
.subscribe(compiledTemplate => console.log(compiledTemplate))

Quasi Elements

Quasi elements are shorthand for text formatting.

The syntax for these is <<[quasi]>>[content]<</[quasi]>>. This will wrap the content with HTML elements based on the definition of that quasi-element. These can be nested as with HTML.

An example of a built in quasi-element is bold which will given the template <<bold>>[content]<</bold>>. output <span style="text-weight: bold">[content]</span>. To make the template less verbose the quasi-identifier for the end tag is optional, so an equivalent template is <<bold>>[content]<</>.

While a span is the default output element it can be modified in the template directly by specifying it in the starting tag <<p:bold>>[content]<</>> which will compile to <p style="text-weight: bold">[content]</p>.

Another helpful feature is that multiple quasi-elements can be specified in a single tag. If you want a piece of text bolded and underlined you do not have to necessarily nest them but specify both directly<<bold,underline>>[content]<</> which will compile to <span style="text-weight: bold; text-decoration: underline">[content]</span>.

Adding custom quasi-elements is very simple:

new Whiskers({
  quasi: { 
    red: { style: { color: 'red' } } 
  }
})

Which allows you to use it in your templates.<<red>>[content]<</>>

Interpolation

Transforms

While quasi-elements and interpolation are both useful, it is the transforms that make whiskers something more than a simple template language.

The idea of a transform is similar to middleware that is injected into the compilation process to manipulate interpolated data before it is included in the text output. The interesting thing with transforms is that they output an observable stream, not a single value. This means that the output of transforms can change over time as needed.

Transform updates do not require a full recompilation of the template. Only the subtree that is affected by that specific transform is updated.

Take this template as an example:

Get ready <<bold>>Game starts in <<red>>{{ count | count-down }}<</red>> seconds<</bold>>

Say that we provide 5 as the count and include a count-down transform that every second decrements an output count until it reaches 1. We would expect the compiled template tree to look something like this.

When the transform updates the output value of the decremented counter, only the subtree from the root to the transform is recompiled, and combined with the already compiled subtrees of the rest of the template.

This is especially important if we are using multiple transforms that might be updating at different times.

Building meta-understanding and automation machines for marketing requires a state-of-the-art time-series platform.  Discover more how Adverai can help your brand on our website or get in touch by email.

Leave a Reply

Your email address will not be published. Required fields are marked *