Using the Thymeleaf view language

On a recent project of ours (Java/SDL Web 8/DD4T 2), the customer’s architect suggested to use a view language that I had – quite frankly – never heard of: Thymeleaf. I had always wondered why the Java world had never settled on a view technology to replace JSP. The way I see it, JSP has had a great run (it’s been around since 1999) but it belongs in the same category as ASP – a moloch from times of old. Although you can use JSP as the ‘V’ in ‘MVC’, it has never been a true view language. The support of inline java code means that developers can easily run amok, which can lead to messy and hard to maintain code bases. Also, JSP writes it output directly to the response, making it less flexible than more modern view technologies.

I have been waiting for a Java equivalent to .NET’s Razor: neat, clean syntax, intuitive switching between code and markup, and very well integrated into the framework. So when Thymeleaf was suggested, I thought – perhaps – this could be it!

Let me tell you right off: Thymeleaf, Sir, is no Razor. But it is definitely an interesting language.

First of all: to use Thymeleaf within your Spring MVC app is ridiculously simple. You only need to define 1 bean to make it work, namely a ThymeleafViewResolver. This is an implementation of org.springframework.web.servlet.ViewResolver.

The ThymeleafViewResolver needs a template engine, which in turn requires a template resolver. The code below shows how you can define a ThymeleafViewResolver bean:

@Bean public ViewResolver viewResolver() {
        SpringResourceTemplateResolver templateResolver = 
             new SpringResourceTemplateResolver();
        templateResolver.setTemplateMode(TemplateMode.HTML);
        templateResolver.setPrefix("views/");
        templateResolver.setSuffix(".html");
        SpringTemplateEngine engine = new SpringTemplateEngine();
        engine.setTemplateResolver(templateResolver);
        ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
        viewResolver.setTemplateEngine(engine);
        return viewResolver;
}

Note that some examples on the Net use a ClassloaderTemplateResolver. However, for Thymeleaf 3 the SpringResourceTemplateResolver is recommended (see http://www.thymeleaf.org/doc/articles/thymeleaf3migration.html).

Now that the preparation is out of the way, let us look at the Thymeleaf views themselves. This is a typical view:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
    <head>
        <title th:text="${page.title}">Title of my page</title>
    </head>
    <body>
        <h1 th:text="${page.header}">Header 1 of my page</h1>
    </body>
</html>

The first thing you notice about this page is the namespace declaration at the top. This is actually not necessary, but it is a proper thing to do.

It gets more interesting if you look at the line starting with <title>. The title element has an attribute ‘th:text’. Thymeleaf loves this kind of attribute, and offers a bunch of them out of the box. This ‘text’ attribute is very common. It takes the expression within the attribute (in this case: ${page.title}) and renders it.

Thymeleaf supports expression language, which you may be familiar with already if you have experience with Java tags. This particular expression means: please call the method getTitle() on the object named ‘page’ and insert the returned value WITHIN the title element.

If the page object has a title ‘My Page Title’, this Thymeleaf view, when rendered, will return the following:

<title>My Page Title</title>

As you can see, the contents of the title tag have been replaced by whatever was in the title property of the page object. Also, the th:text attribute has gone.

The great thing about this style of templating, and the main differentiator (I think) of Thymeleaf, is that the same view can also be displayed by a browser directly, and it would still look great. The text within the title and h1 elements (in the example above) would then show in the browser. This is the reason why most people choose to give their Thymeleaf views the extension .html.

The reason why this is so great, is that you can hand your view over to a front-end developer, and they can run it in their browser, fix whatever was wrong with it, and return it to you. That is much nicer to the more usual approach of maintaining front-end in a separate codebase and copying changes to your views all the time.

I can hear some people object, and sure enough: there are cases where it’s not so easy. Thymeleaf views support conditions, like so:

<div th:if="${page.title != null}">
  <h1 th:text="${page.title}">My title</h1>
</div>

When this snippet is rendered as a Thymeleaf view, and the page’s title is null, there will be no output at all, whereas when rendered in a browser directly, it will contain a div, an h1 tag AND some text inside that tag.

Still, you have to admire the general concept.

Thymeleaf and DD4T

In DD4T (as in all Tridion implementation styles) we make a distinction between page views and component views. I won’t go in to the exact details, but what it comes down to is this: in DD4T 2, a page view operates on an org.dd4t.contentmodel.Page, while a component view operates on an entity which you define yourself, and which corresponds with the component presentations on your page. See http://blog.trivident.com/viewmodel-functionality-in-dd4t-2-0-for-java/  for more information about java ViewModels in DD4T.

A simple example of a page controller that works with DD4T:

@Resource
protected PageFactory pageFactory;

@Resource
protected PublicationResolver publicationResolver;@Controller

public class PageController extends AbstractPageController {

     @Override
     @RequestMapping(value = { "/**/*.html" }, 
        method = { RequestMethod.GET })

     public String showPage(Model model, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException {
          String url = HttpUtils.getCurrentURL(httpServletRequest, true);
          String pubUrl = publicationResolver.getPublicationUrl();
          Page page = pageFactory.findPageByUrl(url, publicationId);
          model.addAttribute("page", page);
          return page.getPageTemplate().getTitle();
     }
 }

Note the use of @Resource to inject all the necessary factories and resolvers into our controller! Spring is great!

 

Tips for Tridion Thymeleaf developers

  1. Be defensive! Fields in Tridion can be optional, which means properties of your ViewModel objects can be null. So check with a ‘th:if’ whether or not it is safe to write out your property.
  2. Be more defensive! Even if your field is mandatory in the schema, it is a small effort to make your view defensive anyway. That way, you minimize the dependency between the web application and the CMS. If someone ever decides to make a field optional, your code will still run.
  3. Create dialects! It’s easy to extend the Thymeleaf language with your own dialect. This is similar to creating taglibs in JSP. A dialect is not the same as a taglib however! You cannot use taglibs in your Thymeleaf views.
    If you feel like writing your own dialect, check out this tutorial: http://www.thymeleaf.org/doc/tutorials/2.1/extendingthymeleaf.html.
  4. Use the DD4T Thymeleaf dialect! Yes, there is one now . This gives you various additions to make your pages ready for inline editing with Experience Manager.

 

Summing up

Now that I’ve worked with it a while, I can say that Thymeleaf is well-designed, stable and fast. But you can say the same for JSP. Compared to JSP, Thymeleaf feels more modern and lighter. The fact that Thymeleaf views can be displayed by a browser directly is a nice bonus. On the other hand, JSP boasts a glorious history of decades. And of course, there are many more JSP developers than Thymeleaf developers out there.

So if you want to try it out for yourself, please do. There’s nothing wrong with at and you may even enjoy it. But if you want to hold out a little longer until the REAL ‘Razor for Java’ comes along, no one can blame you.