Templating

Definition

A domain-specific language and deployment process designed specifically to generate custom text output.

Example Use Cases

  • A web content management system generates web pages. The HTML is generated from a set of templates, separate from the source code.

  • An email marketing system uses templates to form the body of an email. The template is edited through a UI and allows the editor to enter placeholders that are replaced with specific data.

Relation to the Application

Templating is often a separate subsystem, defined along several characteristics:

  • Language: Templates are usually in a language other than the core language of the application. This language has specific features designed for outputting custom text.

  • Computational Space: Template code is often executed in a “sandboxed” computational space, separated from the main application. Errors in templates are (or should be) contained and managed without a negative on the stability of the application.

  • Code Management: Template code is often managed separately from other integration code. It’s sometimes in files in a different storage mechanism, and is sometimes managed directly from the application UI through a browser-based IDE.

  • Intended Developer: A “template developer” is sometimes a separate role than an “integration” or “backend” developer. This developer is usually skilled in front-end technologies (HTML, CSS< JavaScript) and also learns the templating tools of the application.

Sometimes, of course, none of these are true. An application might do templating in the same language as the application, executed in the same process, with files in the same location, coded by the same developer. The differences are a matter of degree.

A key point: the output of a templating operating is usually intended as a final stage of delivery, to be sent to a consuming device or platform. Very rarely is the output of templating re-incorporated into application logic or operated on in any other way. Data is templated at the last moment before delivery, then it leaves the application context.

The Two Philosophies of Templating

There are two competing theories of how templates should interact with the application:

  1. Weak Templates: Templates should be limited in both data and features. They should only be able to interact with data “given” to them, and they should have a very limited logical featureset. Having logic inside templates just creates another level of code and potential issues, often written by a developer not familiar with the larger application.

  2. Strong Templates: Templates should have most of the logical features of the “full” programming language, and should even be able to retrieve data beyond that which is provided to them. A templating language only exists to provide a layer of computational sandboxing, features specifically designed for the manipulation of text, and the convenience of a separate DevOps context.

Most systems will exist somewhere in the middle. In particular, the weak templating philosophy has some vocal adherents for “logic-less” templates, but it imposes considerable limitations on template development that often frustrate developers working with it.

The Template “Context”

Most all templating languages operate on the idea of a special “sandbox” or context in which the template executes. The templating language will have access to the data specifically injected into this context, or the results of functions made available to that context, and nothing else.

context.SetValue("name", "Deane");
template.Render(context);

In the example, a token called “name” will be available inside the template, containing the string value “Deane.”

In some cases, selected data retrieval functions might be available to templates, but this is not common. “Extra” data might be made available to a template, which it can use or ignore, but seldom can a template proactively retrieve data from outside the context.

Tag Structure of Templating Languages

Templating languages are usually designed to be simpler than “full” programming languages. They’re also designed to be intermixed with “markup” of some kind – usually HTML. This means that template code is usually intermixed with markup code, offset by delimiters.

  • Liquid and Twig use {% %} for logic blocks, and {{ }} for output blocks

  • Scriban uses {{ }} for everything

  • Handlebars and Mustache also use {{ }} for output, with the addition of some other tags like {{# }} and {{ / }} for “sections,” which allow looping and simple conditionals

Anything in these tags is either executed (and removed) or replaced with something else (and therefore also removed). Anything outside these tags is part of the generated output.

There are also a few templating languages that abandon the “intermixed tag” formal altogether:

  • HAML (HTML abstraction markup language) uses an tree-style markup format with indenting to represent nesting

  • TAL (Template Attribute Language) uses attributes on HTML tags

  • XSLT uses XML to template other XML

Feature: Token Replacement

As templating code is intermixed with markup code, much of templating involves the insertion of placeholders or tokens which are replaced with data injection into the templating context.

<p>
  <strong>First Name:</strong> {{ person.first_name }}
</p>

In the above example, a data object called person has been injected into the template context. It has a property called first_name which will be retrieved and will take the place of {{ person.first_name }}

Feature: Filters

In many cases, data needs to be formatted or modified before it’s output. The most common metaphor for this is the concept of “piping” data in a command line using the | character.

<p>
  <strong>First Name:</strong> {{ person.first_name | upcase }}
</p>

In that example, the output of person.first_name will be filtered through a filter called upcase, which will presumably convert the text to upper case.

In most systems, filters can be chained and can sometimes take arguments:

{{ person.age | plus:10 | format_number:'N0' }}

Feature: Conditionals

Logic in templates might branch, though it’s much more common to use conditionals to simply display or not display a particular chunk of markup:

{% if person.age >= 55 %}
  <p>You're legible for a senior discount!</p>
{% endif %}

Most templates will support the standard operators: equal, not equal, greater than, lesser than, etc. Some also have a simple negation tag, like unless, which would be the opposite of if.

Given that most templating languages are less exact than full programming languages, and, in particular, less strict about typing, sometimes it’s difficult to determining “nothing-ness.” Does this mean…

  • A null value
  • An empty string
  • An empty collection
  • A numeric value of 0

Every language is little different in this respect. Some include constants like blank, empty, and nil to test against:

{% if articles == empty %}

Feature: Looping

Most languages can iterate over collections of objects.

<ul>
{% for article in articles %}
  <li>{{ article.title }}</li>
{% endif %}
</ul>

Some languages include extra iteration features like:

  • Limiting loops to a specified number of iterations
  • An else block to provide a markup option if the collection is empty
  • A special forloop variable inside the loop which contains information about the loop iteration: the number, whether its odd or even, the first or last, etc.
  • The ability to “cycle” an array – use a different element on each loop (for example: you might provide an array of five CSS classes, and the cycle variable would move between the five on each successive loop, starting back at the first on loop six)

Feature: Inclusion

Like other code, templates tend to be repetitive and derivative, and it’s often that template code is re-used. Therefore, templates can often perform inclusion on two levels

  • Sub-template Inclusion: Template A can include Template B. It might even be able to provide specific data to Template B that can only be accessed and used by Template B. Sometimes it can include and execute Template B for each iteration of a loop.

  • Super-template Inclusion: Template A can “include itself” into Template C. Additionally, Template A might be able to divide itself into different sections, and include the output of those sections into specific places in Template C. This is usually done to generate page-specific output (Template A), then “wrap” that output in a master layout (Template C).

Many templates will perform both types of inclusion:

  • article.tpl includes list_item.tpl inside a loop
  • The result of that operation is then included into text_page.tpl
  • text_page.tpl includes open_graph.tpl and pdf_download_link.tpl
  • The result of all that is then included into master_layout.tpl

Common Templating Systems

Given that templating is pan-application, most languages have a “favored” templating system:

  • .NET: Razor
  • Java: Freemarker and Velocity
  • PHP: Twig

Some other systems have achieved pan-language adoption, with implementations for multiple languages:

  • Liquid
  • Mustache (crucially, this can be used in client-side JavaScript)
  • Handlebars
  • StringTemplate

Very rarely will an application write a custom templating system. It’s much more common for a language to provide custom extensions to an existing language.

Extensibility

Most templating languages allow new functionality to be added via development in the implementing language. Common extension points:

  • Custom tags and blocks
  • Custom formatting filters
  • Custom functions

These manifest as custom strings in template code that invoke code in the native application language and send return the results to the template context.

Practices for User Extensibility: Templating