One thing that I did not like about working with the Nitrogen web framework is the way that view rendering is handled. Nitrogen calls the function main/0 in your web modules which, by convention, returns an instance of the template record. This record points to a template file that has HTML code with a little Erlang mixed in. Essentially, you can call functions in your web module and whatever content those functions return is interpolated into your template.
This all makes it sound a little more complicated than it really is. Here is a little “hello world” sample to illustrate how it works. First, the web_page Erlang module defines the main/0 function and a couple of callouts:
-module(web_page).
-include("wf.inc").
-compile(export_all).
main() -> #template { file="./wwwroot/template.html" }.
title() -> "Hello, World!".
content() -> #h1 { text="Hello, World!" }.
This will cause Nitrogen to render the template.html file.
<html>
<head>
<title>[[[page:title()]]]</title>
</head>
<body>
[[[page:content()]]]
</body>
</html>
The callouts in the template will call the respective functions in the web_page module and we end up with a page that displays a bold “Hello, World!”. The template file is really analogous to a Rails layout and the content that Rails puts into a view template is returned from the callouts using a markup DSL based on the Erlang record syntax.
This template mechanism is simple, but it becomes very limiting when working on a non-trivial application. Nitrogen mixes controller functionality with presentation in a way that is reminiscent of PHP or JSP. Also, you cannot compose templates and the markup DSL doesn’t cover all of the HTML elements and attributes.
I ran into this problem while working on a Nitrogen powered site and decided that I just couldn’t live with the default template system. After a little digging around I discovered ErlyDTL which implements a substantial subset of the Django Template Language in Erlang. It turns out that using ErlyDTL in a Nitrogen site is quite simple.
The first thing I needed was a utility function to render ErlyDTL templates. Nitrogen includes implicit elements for the notification flash and Javascript code for all of that AJAX goodnes which I needed to replicate in my render function.
-module(my_util).
-compile(export_all).
template_path(Template) ->
filename:join([code:priv_dir(my_app), templates, Template]).
render_template(Name, Variables) ->
Template = template_path(Name ++ ".html"),
Mod = list_to_atom(Name ++ "_template"),
ok = erlydtl:compile(Template, Mod),
{ok, Body} = Mod:render([{flash, element_flash:render()},
{script, wf_script:get_script()}
| Variables]),
Body.
Now, I can replace the simple module from earlier with this new one:
-module(web_page).
-include("wf.inc").
-compile(export_all).
main() ->
my_util:render_template("template", [
{title, "Hello, World!"},
{content, content()}]).
content() -> "Hello, World!".
My template file is now equally simple:
<html>
<head>
<title>{{ title }}</title>
</head>
<body>
<h1>{{ content }}</h1>
</body>
</html>
All of the markup is now in the template file, and the module is concerned only with getting the content from somewhere and making it available to the view. This also allows us to use the full power of ErlyDTL: we can compose templates and perform simple operations on the content to be displayed. Here is an example from the ErlyDTL home page:
Welcome back, {{ name }}!
You have {{ friends|length }} friends: {{ friends|join:", " }}
Have some primes:
{# this is exciting #}
{% for i in primes %}
{{ i }}
{% endfor %}
There is one drawback to this approach. The Nitrogen DSL includes helpers for generating links that work with its event system. When using ErlyDTL we have to create those links manually, but it isn’t hard. The key thing to remember is that the link id is the event name that will be passed to your module. Here are a couple of examples:
<a id="do_it" href="javascript:">Do It!</a>
<button id="do_it" class="button submit">Do It!</button>
The extra flexibility of DTL is certainly worth the small amount of extra work.
Tags: dtl, erlang, erlydtl, nitrogen