This post assumes you’ve read all the previous entries in this series. Any variables and functions which haven’t been previously explored, or haven’t been explained fully, will be explained as they appear.
The if
function, and template block hyphens
Including a hyphen inside a template will cause any whitespace on that side of the template block to be collapsed, so some {{- "" -}} thing
would be output as something, which is useful for preventing extra whitespace or newlines from being added because of template blocks themselves.
It’s worth emphasising that {{ example -}} {{- example }}
is redundant; you only need one of the hyphens to remove the whitespace between them, and, I recommend being consistent with which hyphen you include. For example, I always use left hyphens, for cases where only one hyphen is needed, so it’s easier to tell when double hyphens are present.
The with
function
Unlike the if
function, you can’t use elif
inside with
statements; only the else
function is valid. However, you can nest an if
statement under else
instead, like this:
The range
function
In the previous posts of this series, I’ve shown off basic uses of the range
function to do simple tasks, however, there are times where you’ll need the more advanced functionality it can provide. As an example, let’s say you wanted to print the .Title of each page under the .Pages variable, and you wanted that page’s index in that variable. To accomplish that, you can use the range
function’s indexing feature:
You must define a variable in the place
$page
is located, otherwise you’ll get a parse failed range can only initialize variables error.
This will print the index, which starts counting from 0, and the .Title variable for the page at that index.
As an even more specific example, which I use myself, let’s say you wanted to range over the .Params.series and .Params.tags variables together (to save needing separate range
statements for each variable), but, you wanted a way to track which taxonomy each term came from so that tags would be given <a rel="tag directory">Tag</a>
and series would be given <a rel="category tag directory">Series</a>
.
To achieve this, you can use the dict
function to define a dictionary of key-value pairs, and then, the index of the range
function will be the key’s name. To demonstrate:
You could use either $term or . inside the
<a>
element’s content, however, a deeper nesting of functions may require you to use $term once context rebinding enters the mix.
Additionally, you can use the range
function on the $term variable as a way to sneak the sort
function in there too:
Notice the use of the
default
function to deal with both the .Params.series and .Params.tags variables being unset, read the Simpler, robust logic post for more information.
I do want to stress one limitation with the dict
function: It will unconditionally sort its entries by key name such that, in this case, the series tags will be handled before the regular tags since “series” comes before “tags” alphanumerically; the sort
function only affects the values under each key. In other words, the dict
function ignores the order you specify the key-value pairs in.
There might be a way around this using the group
function instead of dict
, however, my previous attempts at doing this were unsuccessful. Of course, if you wanted to change the order, you could just change the names of the keys to achieve the order you want, but it’s annoying if you want to print the $tax value as is (which is what I do, but luckily, I actually like the ordering).
The return
function, and “custom functions”
As of writing, Hugo doesn’t support custom functions without changing the code of the hugo binary (see the Hugo GitHub repo for progress on that front), but until then, you can achieve something similar using partials. For example, let’s say you wanted a custom function which outputs a string after its been fed through multiple sanitising functions such as markdownify
and plainify
:
You can’t execute the
return
function multiple times in a single partial, but, you can useslice
ordict
to “return” multiple values.
Now, you can use {{ partial "sanitise.html" "Test *str­ing*" }}
and receive Test string as the output. But, what if you wanted to pass an argument to this “function” too?
With this change, you can specify {{ partial "san.html" (dict "arg" "nosafe" "val" "Test *str­ing*") }}
to omit the safeHTML
function, but, you need to always use the dict
function to specify the value of “val”.
This partial could likely be improved so you could pass the value both with or without the
dict
function, however, I’ll leave that as an exercise for you.