Skip to content
Get started

Template syntax

Doquill templates use Go’s html/template engine. Everything between {{ and }} is an action; everything else is copied to the output as-is. This page covers what you need for documents. Doquill’s own functions are listed in Template functions.

The JSON data you send to the render endpoint is the starting value of . (“dot”). Reach into it with field names:

Data
{ "number": "INV-1042", "customer": { "name": "Acme Corp" } }
main.html
<h1>Invoice {{ .number }}</h1>
<p>Bill to {{ .customer.name }}</p>

Field names are case-sensitive and must match your JSON keys exactly. A key that’s missing from the data renders as nothing, without an error, so a typo shows up as a blank in the document. Give your template a JSON Schema that marks the fields it needs as required.

Keys that aren’t valid identifiers, such as "line-items", can be read with index: {{ index . "line-items" }}.

{{ if .paid }}
<span class="badge">Paid</span>
{{ else if .overdue }}
<span class="badge warning">Overdue</span>
{{ else }}
<span class="badge">Due {{ dateFormat "Jan 2" .due_at }}</span>
{{ end }}

if is false for false, 0, null, an empty string, an empty list and an empty object, and true for anything else.

range repeats its body for each item of a list. Inside the loop, . is the current item:

<table>
{{ range .items }}
<tr>
<td>{{ .description }}</td>
<td>{{ currency "€" 2 .unit_price }}</td>
</tr>
{{ else }}
<tr><td colspan="2">No items</td></tr>
{{ end }}
</table>

The optional {{ else }} branch renders when the list is empty. To get the position as well, name both values. The position starts at 0, so this puts commas between names:

{{ range $i, $item := .items }}{{ if $i }}, {{ end }}{{ $item.name }}{{ end }}

Because . changes inside range, use $ to reach the top-level data: {{ range .items }}{{ .description }} ({{ $.currency }}){{ end }}.

with sets . to a value and skips the block when the value is empty:

{{ with .customer.vat_id }}
<p>VAT ID: {{ . }}</p>
{{ end }}
{{ $total := mul .quantity .unit_price }}
<td>{{ currency "€" 2 $total }}</td>

A variable lives until the end of the block (if, range, with) it was declared in.

| passes the result of one command as the last argument of the next. Doquill’s functions take the formatted value last for this reason:

{{ .total | round 2 | currency "€" 2 }}
{{ .name | trim | upper }}

Parentheses group a call when you need its result as an argument: {{ currency "€" 2 (mul .quantity .unit_price) }}.

Any .html file other than the entry file, header.html and footer.html is a partial. Call it with template, passing the data it should see as .:

{{ range .items }}
{{ template "line-item.html" . }}
{{ end }}

A - inside the braces trims whitespace on that side, including newlines: {{- .name -}}. This matters where whitespace is visible, such as inside a white-space: pre element or between inline elements.

{{/* This is not rendered */}}

Output is escaped for its context: HTML text, attributes, URLs and CSS each get the right escaping automatically, so data like <b> shows up as text instead of becoming markup. There is no way to switch escaping off for data. Functions that return markup, such as qrcode, are marked safe and render as-is.

Go’s built-in functions are available alongside Doquill’s.

Function Use
eq a b a == b. With more arguments, true if a equals any of them: eq .status "paid" "refunded".
ne a b a != b
lt, le, gt, ge <, <=, >, >=: {{ if gt .total 1000.0 }}
and a b, or a b, not a Boolean logic. and and or stop at the first deciding value.
len x Length of a list, object or string: {{ len .items }} items
index x key… Element of a list or object by position or key: {{ index .items 0 }}
slice x i j Part of a list or string: {{ slice .reference 0 4 }}
printf format args… Go’s fmt.Sprintf. JSON numbers are floating-point, so use %f verbs: {{ printf "%05.0f" .sequence }} gives 00042.
print, println Concatenate values as text.
html, js, urlquery Escape explicitly. Rarely needed, since escaping is automatic.