Simple comes with support for the embedded templating language defined in the package
simple-templates. Templates let you embed dynamic content in HTML, JSON or any other text format you return in your responses.
Using with a Simple app
Adding template support in your app is as easy as declaring an instance of
HasTemplates for your app settings:
data MyAppSettings = ...
instance HasTemplates MyAppSettings
HasTemplates has default definitions for all of its methods. However, in most cases you’ll probably want to override at least:
A template may contain plain-text, which is reproduced as is, as well as blocks of code, denoted by encapsulating them with dollar-signs ($), supporting variable expansion, function invocation, conditionals and loops. For example, given a global variable “answer” with the value 42,
The answer to the universe is $answer$.
would expand to
The answer to the universe is 42.
Since the dollar-sign is used to denote code sections, it must be escaped in plaintext sections by typing two dollar-signs. For example, to reproduce the lyrics for Bonzo Goes to Bitburg, by The Ramones:
Shouldn't wish you happiness,
wish her the very best.
Shaking hands with your highness
Booleans, Numbers, Strings, Arrays and Null values can be typed as literals:
- Booleans are the lower-case
- Numbers are rationals, parsed according to the rules in attoparsec’srational parser (roughly, decimal numbers with an optional decimal point and optional exponent)
Pi is approximately $3.14159$
- String literals are surrounded by double-quotes (“). Double-quotes inside a string can be escaped by proceeding it with a backslash (\”), however backslashes themselves do not need to be escaped:
And then, Dr. Evil said:
$"Mini Me, stop humping the \"laser\"."$
- Arrays are surrounded by square-brackets ([ ]) and elements are comma separated. Elements can be literals, variables or function invokations, and do not have to be the same type. Spaces between elements are ignored:
$["Foo", 42, ["bar", "baz"], length([1, 2, 3, 6])]$
- Null is typed as the literal null (in lower case):
- Objects map from String keys to values of any type. Objects cannot be typed literally.
Templates are evaluated with a single global variable called
@. For example, you can reference the global in your template like so:
The value in my global is $@$.
If the global is an Object, it can be indexed using dot-notation:
The Sex Pistols' bassist was $@.bassist.name.first$
In this case, you may also discard the
@ global reference and simply name the field in the global object, for example:
Field 'foo' is $foo$.
Field 'bar.baz' is $bar.baz$.
Strings, Numbers and Booleans are meaningful when evaluated to text in a template. Objects and Arrays render as a string representing their types (e.g. “[object]”). Null renders the empty string. However, values of all types can be used as arguments to functions, or in conditionals and loops.
Functions are invoked with similar syntax to imperative languages:
$myfunc(arg1, arg2, arg3)$
where arguments can be literals, variables or other function calls – basically anything that can be evaluated can be an argument to a function. Function names are in a separate namespace than variables, so there can be a function and variable both named foo and they are differentiated by their use. For example:
is a variable expansion, whereas
is a function invocation.
Branching is supported through the common if statement with an optional elsebranch. Conditions can be any expression. false and null are evaluated as false, while everything else is evaluated as true.
if blocks are surround by an if-statement and and endif, each surrounded separately by dollar signs. Optionally, the else branch is declared by with “$else$”. The blocks themselves are templates and may contain regular text as well as evaluable expressions.
Should I stay or should I go?
Trouble will be $trouble$.
Trouble will be $double(trouble)$
For loops iterate over collections, setting a variable name to one element in the collection for each iteration of the loop. Collections are usually
Arrays, however non-false expressions (e.g.,
Numbers) are treated as collections with one element. A loop starts with a for-statement surrounded by dollar-signs and end with an “$endfor$”:
$for(member in band)$
<li>$member.name$ played the $member.instrument$</li>
There is also an optional “$sep$” (for separator) clause, which is renderedbetween iterations. So if I have a collection with three items, the sep clause will be rendered after the first and second, but not third elements:
$for(item in groceries)$
Will render something like: