Distigme

A little learning is a dangerous thing.

Why Thymeleaf?

1 Comment

Not long ago I started a new web project in Java (not my choice of language) and rediscovered what a pain that old dinosaur JSP is. Looking for more modern replacements, I saw some suggesting JSP’s logical successor JSF (ugh, ASP.NET-style postbacks!). Many considered FreeMarker the best available. And then there’s the new kid on the block, Thymeleaf, which I fell in love with.

Coming from JSP—or more precisely JSPX, formally JSP Document, which is the only sane way to do JSP—I tend to focus on its shortcomings, and how Thymeleaf improves upon them.

So, what’s so great about Thymeleaf?

First, what I’m trying to create is HTML5. What JSPX makes for me is XML. The creators of JSPX probably thought we’d all be serving XHTML by now, but instead we got HTML5. Most unfortunately, XML is not HTML. If you need reminding of that, here are a few classic gotchas.

  1. Never self-close a <script> or <div> tag. This won’t work:

    <script src="foo.js" />
    <div style="color:red" />
    <h1>Hello, world!</h1>
    

    Even if you remember and try to do the right thing, JSPX will strip any white space and collapse the element back into self-closing form; workarounds (my favorite its to insert ${null}) are container-specific. Without the closing tag, this <script> will just do nothing and this <div> will make the rest of the document red.

  2. Double-escape everything except <script> and <style>. This JSPX won’t work:

    <div>Chapter title: &lt;script&gt;alert("Augh! I've been hacked!")&lt;/script&gt;</div>
    

    You end up actually injecting a script. Imagine if this chapter title is filled in dynamically from user-entered data, and boom, XSS attack. So, you learn to use <c:out> everywhere, but then you get to a script and try this, which won’t work:

    <script>
    var myTitle = "<c:out value="${myTitle}" />";
    </script>
    

    It turns out that <script> elements have a CDATA content model, which means that everything except </ is treated literally. Your chapter title will get mangled by HTML-escaping. You have to remember that you instead need Javascript string escaping here (thank you, Spring).

    The trickiest scenario is when you try to inject JSON:

    <script>
    var myJson = ${myJson};
    </script>
    

    Now, JSON is almost, but not quite, suitable for injecting right in like this. After all, it is valid JavaScript. But JSON will let you have a string like "</script>", whereas HTML will see this as the abrupt end of your script block (BTW, if you use CDATA sections in scripts in XHTML, you have the same problem with ]]>). You can escape </ into <\/, but you have to remember to do it, and it will never be obvious when you forget.

  3. Boolean attributes are a bit odd. HTML, in contrast to XML, doesn’t actually require an attribute value at all (old browsers even required the value to be absent!). HTML4 requires any value, if present, to be the same as the attribute name, while HTML5 allows any value. So what does this do?

    <input type="checkbox" checked="${2 + 2 == 5}" />
    

    The expression evaluates to false, so you get the attribute checked="false", which in modern browsers causes the box to be checked. Wait, that isn’t what you meant?

The upshot of all this is that any usable template system for serving HTML5 needs to actually know the idiosyncrasies of HTML5 and not just XML. In fact, the ideal target is polyglot XHTML5. For me, this is indispensable, and this is where Thymeleaf shines.

The feature that the creators of Thymeleaf seem to emphasize, though, is natural templating. If you open a .jspx file directly in the browser, you get a hot mess, but if you open a Thymeleaf .xhtml file instead, you get something very close to the live page. Then your designer can go to work.

When you have shared content such as a menu bar that you don’t want to copy-and-paste onto each page, you can use Thymeleaf’s th:include to pull in that fragment. That breaks your static-file preview, of course, but Thymol comes to the rescue and does the right thing with some JavaScript goodness.

Thymeleaf uses attributes almost exclusively, in sharp contrast to JSP relying on elements. And it consistently does the right thing for escaping text. The natural templating works primarily by supplying an optional placeholder and using Thymeleaf attributes, conventionally namespace-prefixed with th:, to replace that with a dynamic value at runtime. Other measures are in place for repetitive data such as tables. For example,

<h1 th:text="${myTitle}">Title goes here</h1>

I have reservations about relying on this approach very much. For things like localized text, the placeholders end up being a lot of duplication. You can easily end up changing the placeholders rather than the real data by mistake, or letting the two get out of sync. For something like an image or stylesheet URL, your placeholder needs to be a relative path in your project, which is annoying and again can get out of sync. Furthermore, you can edit a page and reload it without restarting the entire servlet, so working on the live page really isn’t that big a pain. So, basically, I’m not sure the extra work to make natural templating usable for offline design is worth it.

Another really handy feature of Thymeleaf is handling of URLs and localized text. In JSPX, even with Spring already helping out a lot, you would have to do something like:

<spring:url var="urlSmiley" value="${'/img/smiley.png?rating=' + rating}" />
<spring:message var="msgHappiness" value="Menu.Happiness" />
<img src="${urlSmiley}" title="${msgHappiness}" />

Thymeleaf’s expression dialect has a single-character shorthand for each:

<img src="@{/img/smiley.png(rating=${rating})}" title="#{Menu.Happiness}" />

It’s still a young project, and the developers are active and responsive. The future for Thymeleaf looks bright.

Advertisements

One thought on “Why Thymeleaf?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s