Apio uses Go’s text/template
package for templates for speed, reliability and security.
The following is only a specific primer on the frequently used Go Templates. For an in-depth look into Go Templates, check the official Go docs . Go Templates provide an extremely simple template language that adheres to the belief that only the most basic of logic belongs in the template.
Go Templates are text files with the addition of variables and functions. Go Template variables and functions are accessible within {{ }}
as below:
Text {{some go template call}} text {{ another call }}
The above example will fail to compile but shows the structure of template tags and the possibility to add extra padding spaces in side the tags.
A variable could be a variable already existing in the current scope (like the .Payload.OrderNumber
example below) or a custom variable (like the $address
example in that same section).
{{ .Payload.OrderNumber }}
{{ $address := .Payload.address }}
{{ $address.firstname }}
Scope – the flow payload we are processing – has a .Payload
, and a .Header
part.
Each Go Template gets a data object. In Apio, each template is passed a so-called Bundle
. In the below example, .Payload.OrderNumber
is one of the elements accessible in that Bundle
.
With the Bundle
being the default scope of a template, the Payload.OrderNumber
element in current scope (.
– "the ") is accessible simply by the dot-prefix (.Payload.OrderNumber
):
<OrderNumber>{{ .Payload.OrderNumber }}</OrderNumber>
Values can also be stored in custom variables and referenced later:
The custom variables need to be prefixed with $
.
{{ $a := "Some value" }}
{{ $a }}
The example prints "Variable is APIO" if we have no data, and "Var is Data" if .Payload.data
is not empty:
{{ $v := "APIO" }}
{{ if ne .Payload.data "" }} {{ $var = "not APIO" }}
{{ end }}
Variable is {{ $var }}
Methods and Fields are Accessed via dot Notation
Accessing the Page Parameter bar
defined in a piece of content’s front matter.
Parentheses Can be Used to Group Items Together
{{ if or (isset .Params "alt") (isset .Params "caption") }} Caption {{ end }}
Go Templates only ship with a few basic functions but also provide a mechanism for applications to extend the original set.
Apio template functions provide additional functionality specific to building websites. Functions are called by using their name followed by the required parameters separated by spaces. Template functions can be added by opening a pull request to the developer team on https://github.com/gpmd/gotemplate . Parameters for functions are separated using spaces. The general syntax is:
{{ FUNCTION ARG1 ARG2 .. }}
The following example calls the add
function with inputs of 1
and 2
:
{{ add 1 2 }}
<!-- prints 3 -->
{{ lt 1 2 }}
<!-- prints true (i.e., since 1 is less than 2) -->
Currently used in Apio:
"printf": printf,
"range": range,
"formatUKDate": formatUKDate,
"ukdatetime": ukdatetime,
"ukdate": ukdate,
"unixtimestamp": timestamp,
"nanotimestamp": nanotimestamp,
"dateFrom": dateFmtLayout,
"date": dateFmt,
"timeformat": timeFormat,
"timeformatminus": timeFormatMinus,
"limit": limit,
"fixlen": fixlen,
"fixlenr": fixlenright,
"sanitise": sanitise,
"sanitize": sanitise,
"last": last,
"reReplaceAll": reReplaceAll,
"replace": replace,
"match": regexp.MatchString,
"title": strings.Title,
"timestamp": timestamp,
"datetime": datetime,
"json": asJSON,
"tojson": jsonDecode,
"json_decode": jsonDecode,
"json_encode": jsonEncode,
"xml_decode": xmlDecode,
"xml_encode": xmlEncode,
"toUpper": strings.ToUpper,
"upper": strings.ToUpper,
"toLower": strings.ToLower,
"lower": strings.ToLower,
"filter": filterPath,
"concat": concat,
"empty": empty,
"int": toint,
"float": tofloat,
"ifthen": conditional,
"elseifthen": notconditional,
"mapto": mapto,
"decimal": decimalFmt,
"item": item,
"add": add,
"sub": subtract,
"div": divide,
"mul": multiply,
"var": newVariable,
"explode": explode,
"in_array": inArray,
"unique": unique,
"setItem": setItem,
"createMap": createMap,
"mkSlice": mkSlice,
"escape": escape,
Go Templates provide the most basic iteration and conditional logic.
The Go Templates make heavy use of range
to iterate over a map, array, or slice. The following are different examples of how to use range
.
Example 1: Using Context (.
)
{{ range $array }} {{ . }} <!-- The . represents an element in $array -->
{{ end }}
Example 2: Declaring a variable name for an array element’s value
{{ range $elem_val := $array }} {{ $elem_val }}
{{ end }}
Example 3: Declaring variable names for an array element’s index and value
For an array or slice, the first declared variable will map to each element’s index.
{{ range $elem_index, $elem_val := $array }} {{ $elem_index }} -- {{ $elem_val }}
{{ end }}
Example 4: Declaring variable names for a map element’s key and value
For a map, the first declared variable will map to each map element’s key.
{{ range $elem_key, $elem_val := $map }} {{ $elem_key }} -- {{ $elem_val }}
{{ end }}
See conditionals below for adding comma into a looped variable (like generating JSON output)
if
, else
, with
, or
, and and
provide the framework for handling conditional logic in Go Templates. Like range
, each statement is closed with an {{ end }}
.
Go Templates treat the following values as :
false
(boolean)- 0 (integer)
- any zero-length array, slice, map, or string
Example 1: with
It is common to write "if something exists, do this" kind of statements using with
.
with
rebinds the context .
within its scope (just like in range
).
It skips the block if the variable is absent, or if it evaluates to "false" as explained above.
{{ with .Params.title }} <h4>{{ . }}</h4>
{{ end }}
Example 2: with
.. else
Below snippet uses the "description" front-matter parameter’s value if set, else uses the default .Summary
Page variable : {{ with .Param "description" }} {{ . }}
{{ else }} {{ .Summary }}
{{ end }}
Example 3: if
An alternative (and a more verbose) way of writing with
is using if
. Here, the .
does not get rebinded.
Below example is "Example 1" rewritten using if
:
{{ if isset .Params "title" }} <h4>{{ index .Params "title" }}</h4>
{{ end }}
Example 4: if
.. else
Below example is "Example 2" rewritten using if
.. else
, and using isset
function + .Params
variable (different from the .Param
function) instead:
{{ if (isset .Params "description") }} {{ index .Params "description" }}
{{ else }} {{ .Summary }}
{{ end }}
Example 5: if
.. else if
.. else
Unlike with
, if
can contain else if
clauses too.
{{ if (isset .Params "description") }} {{ index .Params "description" }}
{{ else if (isset .Params "summary") }} {{ index .Params "summary" }}
{{ else }} {{ .Summary }}
{{ end }}
Example 6: and
& or
{{ if (and (or (isset .Params "title") (isset .Params "caption")) (isset .Params "attr")) }}
Adding comma in a loop ignoring the first occurance:
[
{{range $i, $value := .Payload}}
{{if $i}},{{end -}}
"{{$value}}"
{{end}}
]
The trick above is that $i will go from 0 to {{len .Payload}}
, on 0 we don't output a comma (before the first item).
One of the most powerful components of Go Templates is the ability to stack actions one after another. This is done by using pipes. Borrowed from Unix pipes, the concept is simple: each pipeline’s output becomes the input of the following pipe.
Because of the very simple syntax of Go Templates, the pipe is essential to being able to chain together function calls. One limitation of the pipes is that they can only work with a single value and that value becomes the last parameter of the next pipeline.
A few simple examples should help convey how to use the pipe.
The following two examples are functionally the same:
{{ shuffle (seq 1 5) }}
{{ (seq 1 5) | shuffle }}
The following accesses the page parameter called "disqus_url" and escapes the HTML. This example also uses the index
function , which is built into Go Templates: {{ index .Params "disqus_url" | html }}
{{ if or (or (isset .Params "title") (isset .Params "caption")) (isset .Params "attr") }}
Stuff Here
{{ end }}
Could be rewritten as
{{ if isset .Params "caption" | or isset .Params "title" | or isset .Params "attr" }}
Stuff Here
{{ end }}
By default, Go Templates remove HTML comments from output. This has the unfortunate side effect of removing Internet Explorer conditional comments. As a workaround, use something like this:
{{ "<!--[if lt IE 9]>" | safeHTML }} <script src="html5shiv.js"></script>
{{ "<![endif]-->" | safeHTML }}
Alternatively, you can use the backtick (```) to quote the IE conditional comments, avoiding the tedious task of escaping every double quotes ("
) inside, as demonstrated in the examples in the Go text/template documentation: {{ `<!--[if lt IE 7]><html class="no-js lt-ie9 lt-ie8 lt-ie7"><![endif]-->` | safeHTML }}
The most easily overlooked concept to understand about Go Templates is that {{ . }}
always refers to the .
- In the top level of your template, this will be the data set made available to it.
- Inside of an iteration, however, it will have the value of the current item in the loop; i.e.,
{{ . }}
will no longer refer to the data available to the entire packet, not advisable though.
The following shows how to define a variable independent of the context.
{{ $o := .Payload.order }}
<ul>
{{ range .Payload.items }} <li> <a href="/tags/{{ . | urlize }}">{{ .sku }}</a> - {{ $order.customer.priceband }} {{.product_name}} </li>
{{ end }}
</ul>
Notice how once we have entered the loop (i.e. range
), the value of {{ . }}
has changed. We have defined a variable outside of the loop ({{$title}}
) that we’ve assigned a value so that we have access to the value from within the loop as well.
$
has special significance in your templates. $
is set to the starting value of .
("the dot") by default. This is a documented feature of Go text/template . This means you have access to the global context from anywhere. Here is an equivalent example of the preceding code block but now using $
to grab .Site.Title
from the global context:range-through-tags-w-global.html
<ul>
{{ range .Params.tags }} <li> <a href="/tags/{{ . | urlize }}">{{ . }}</a> - {{ $.Site.Title }} </li>
{{ end }}
</ul>
The built-in magic of $
would cease to work if someone were to mischievously redefine the special character; e.g. {{ $ := .Site }}
. Don’t do it. You may, of course, recover from this mischief by using {{ $ := . }}
in a global context to reset $
to its default value.
Go 1.6 and above includes the ability to trim the whitespace from either side of a Go tag by including a hyphen (-
) and space immediately beside the corresponding {{
or }}
delimiter.
For instance, the following Go Template will include the newlines and horizontal tab in its HTML output:
<div> {{ .Title }}
</div>
Which will output:
<div> Hello, World!
</div>
Leveraging the -
in the following example will remove the extra white space surrounding the .Title
variable and remove the newline:
<div> {{- .Title -}}
</div>
Which then outputs:
<div>Hello, World!</div>
Go considers the following characters whitespace:
- space
- horizontal tab
- carriage return
- newline
In order to keep your templates organized and share information throughout your team, you may want to add comments to your templates. There are two ways to do that in Apio.
Go Templates support {{/*
and */}}
to open and close a comment block. Nothing within that block will be rendered.
For example:
Will render Bonsoir, Eliott.
, and not care about the syntax error (add 0 + 2
) in the comment block.
Apio provides the option of passing environment variables to your template through your app environment variables (accessible in the build list screen of your app) . You can define any key-value pairs, where both key and value are string.
As an example in environment variables screen let's define a variable called api_url
with value https://cloud.apio.rocks/api/CA56G90L (just an example), to use this as a prefix for any API endpoint in the URL field for a HTTP step we can type: The resulting URL will be https://cloud.apio.rocks/api/CA56G90L/v1/orders while running the app. The =< and >= are a special delimiter handles, the dot before api_url will tell the templating language that we are using a value from the provided environment variables. In theory we could use built in functions inside =< >= tags but normally there is no need for this and makes checking for environment variables programmatically harder, so any other syntax is discouraged.
You can use Go templates’ printf
function to debug your Apio templates. These snippets provide a quick and easy visualization of the variables available to you in different contexts.
Here are some snippets you can add to your template to answer some common questions.
These snippets use the printf
function available in all Go templates. This function is an alias to the Go function, fmt.Printf . In theory ou can use the template syntax .
, to get the top-level context from anywhere in your template. This will print out all the values under, .Payload
with go type declaration syntax.
{{ printf "%#v" .Payload }}