Djula is a Common Lisp port of the Django templating language. It’s good, it’s proven (it’s one of the most downloaded Quicklisp packages), it is easy to use and it has good documentation.
It basically looks like this:
{% extends "base.html" %}
{% block title %}Memberlist{% endblock %}
{% block content %}
<ul>
{% for user in users %}
<li><a href="{{ user.url }}">{{ user.username }}</a></li>
{% endfor %}
</ul>
{% endblock %}
What was missing in the documentation was how to create custom filters. Here’s how, and it’s very simple.
def-filter
Use the def-filter
macro. Its general form is:
(def-filter :myfilter-name (value arg)
(body))
It always takes the variable’s value as argument, and it can have one required or optional argument. For example, this is how those built-in filters are defined:
(def-filter :capfirst (val)
(string-capitalize (princ-to-string val)))
This is all there is to it. Once written, you can use it in your templates. You can define a filter wherever you want and there is no need to register it or to import it in your templates.
Here’s a filter with a required argument:
(def-filter :add (it n)
(+ it (parse-integer n)))
and with an optional one:
(def-filter :datetime (it &optional format)
(let ((timestamp …))))
When you need to pass a second argument, make your filter return a
lambda function and chain it with the with
filter:
(def-filter :replace (it regex)
(lambda (replace)
(ppcre:regex-replace-all regex it replace)))
(def-filter :with (it replace)
(funcall it replace))
Now we can write::
{{ value | replace:foo | with:bar }}
Note: we should most probably be able to define filters with two arguments. There’s an open issue about that.
Error handling
Errors are handled by the macro, but you can handle them and return a
template-error
condition:
(def-filter :handle-error-filter (it)
(handler-case
(do-something)
(condition (e)
(template-error "There was an error executing this filter: ~A" e))))
It will be rendered on the browser with a nice stacktrace.
Final words
If you don’t know what template engine to use for your web project,
start with it. My only criticism is that accessing variables is not
totally flexible. The {{ obj.val }}
syntax already works to access
objects’ slots, alists, plists, hash-tables and whatnot (it uses the
excellent
Access
library), but it won’t work for some data (like structures), forcing
you to a bit of pre-processing before rendering the template. And you
can’t use much logic with template tags. However, this is by
design. Djula is a port of the Django templating engine after all.
For more flexible templates and still write html (because, you know, we can copy-paste examples easily!), see Eco. See more templates engines in the Awesome-cl list.
Last-minute addition: while I was writing this, Djula’s author released TEN, another templating engine, combining the best of Djula and Eco.