Thymeleaf Tutorial: Chapter 17. Shared Template Logic

  • Tutorial
Table of contents

17 Separated Pattern Logic


17.1. Divided Logic: A Concept


So far, we have worked in our Grocery Store with templates executed in the usual way, with logic inserted into our templates as attributes.

But Thymeleaf also allows you to completely separate template markup from logic, allowing you to create completely logic-free markup templates in HTML and XML template modes.

The main idea is that the logic of the template will be defined in a separate logical file (more precisely, a logical resource, since it does not have to be a file). By default, this logical resource will be an additional file located in the same place (for example, a folder) as a template file with the same name, but with the extension .th.xml:

/templates
+->/home.html
+->/home.th.xml

Thus, the home.html file can be completely logical. It might look like this:

<!DOCTYPE html><html><body><tableid="usersTable"><tr><tdclass="username">Jeremy Grapefruit</td><tdclass="usertype">Normal User</td></tr><tr><tdclass="username">Alice Watermelon</td><tdclass="usertype">Administrator</td></tr></table></body></html>

There is absolutely no Thymeleaf code. This is a template file that a designer with no knowledge of Thymeleaf could create, edit, and / or understand. Or an HTML snippet provided by some external system without any Thymeleaf hooks.

Let's now turn this home.html template into a Thymeleaf template by creating an additional home.th.xml file as follows:

<?xml version="1.0"?><thlogic><attrsel="#usersTable"th:remove="all-but-first"><attrsel="/tr[0]"th:each="user : ${users}"><attrsel="td.username"th:text="${user.name}" /><attrsel="td.usertype"th:text="#{|user.type.${user.type}|}" /></attr></attr></thlogic>

Here we see many <attr> tags inside the thlogic block . Those <attr> tags insert attributes on the nodes of the source template selected using their sel attributes that contain Thymeleaf markup selectors (actually AttoParser markup selectors ).

Also note that <attr> tags can be nested so that their selectors are added. For example, this sel = "/ tr [0]" will be treated as sel = "# usersTable / tr [0]" . The selector for username <td> will be treated as sel = "# usersTable / tr [0] // td.username" .

Therefore, as soon as they are combined, both files seen above will be like this:

<!DOCTYPE html><html><body><tableid="usersTable"th:remove="all-but-first"><trth:each="user : ${users}"><tdclass="username"th:text="${user.name}">Jeremy Grapefruit</td><tdclass="usertype"th:text="#{|user.type.${user.type}|}">Normal User</td></tr><tr><tdclass="username">Alice Watermelon</td><tdclass="usertype">Administrator</td></tr></table></body></html>

It looks more familiar and more convenient than creating two separate files. But the advantage of split templates is that we can provide our templates with full Thymeleaf independence and therefore better maintainability in terms of design.

Of course, it will require contracts between designers or developers, for example, the fact that <table> users will need id = "usersTable" , but in many scenarios a template with pure HTML will be a much better artifact of communication between teams of developers and designers.

17.2 Configuring shared templates


Enabling split patterns Split

logic will not be expected for each pattern by default. Instead, customized template resolvers (ITemplateResolver implementations) will have to specifically label the templates that they find using shared logic.

With the exception of StringTemplateResolver (which does not allow shared logic), all other ready-made versions of ITemplateResolver will provide a flag called useDecoupledLogic , which will mark all templates found by this resolver as potentially having all or part of their logic in a separate resource:

final ServletContextTemplateResolver templateResolver = 
        new ServletContextTemplateResolver(servletContext);
templateResolver.setUseDecoupledLogic(true);

Mixing Connected and

Separated Logic Separated pattern logic is not a requirement. When enabled, this means that the engine will search for a resource containing shared logic, parsing and merging with the original template, if one exists. If there is no shared logical resource, an error will not be generated.

In addition, in the same template, we can mix both combined and split logic, for example, adding some Thymeleaf attributes to the source file of the template, but leaving others for a separate shared logical file. The most common case for this is to use the new ref attribute (in v3.0) th: ref .

17.3 th: ref attribute


th: ref is a marker attribute only. It does nothing in terms of processing and simply disappears when the template is processed, but its usefulness is that it acts as a markup reference, that is, it can be resolved by name from the markup selector in the same way as a tag name or fragment ( th: fragment ).

Therefore, if we have a type selector:

<attrsel="whatever".../>

This will match:

  • Any <whatever> tag
  • Any tag with th: fragment = "whatever" attribute
  • Any tag with th: ref = "whatever" attribute

What is the advantage of th: ref against, for example, using the pure-HTML id attribute? It's just a fact that we may not want to add so many id and class attributes to our tags in order to act as logical bindings that could ultimately pollute our output.

And yet, what is the drawback of th: ref ? Obviously, we will add some Thymeleaf logic to our templates.

Please note that the applicability of the th: ref attribute is applicable not only to split logical template files: it works the same in other types of scripts, for example, in fragment expressions (~ {...}) .

17.4 Split Template Performance


The impact is very small. When the found template is marked for using split logic and it is not cached, the template logic will first be analyzed and processed as a sequence of instructions in memory: basically a list of attributes that will be inserted into each markup selector.

But this is the only additional step, because after that the real template will be parsed, and while it is parsed, these attributes will be injected on the fly by the parser itself, thanks to the advanced node selection options in AttoParser . Thus, the analyzed nodes will exit the analyzer as if they had entered attributes recorded in the source template file.

The biggest advantage of this? When a template is configured for caching, it will be cached already containing the entered attributes. Thus, the overhead of using split templates for cached templates, as soon as they are cached, will be absolutely zero.

17.5. Split logic analysis


Thymeleaf finds shared logical resources matching each pattern and is user-configurable. It is defined by the extension point, org.thymeleaf.templateparser.markup.decoupled.IDecoupledTemplateLogicResolver, for which the default implementation is provided: StandardDecoupledTemplateLogicResolver.

What does this standard implementation do?

  • First, it applies the prefix and suffix to the base name of the template resource (obtained using the ITemplateResource # getBaseName () method ). Both prefix and suffix can be configured and by default the prefix will be empty and the suffix will be .th.xml
  • Secondly, it asks the template resource to find the relative resource with the calculated name using the ITemplateResource # relative (String relativeLocation) method

The specific implementation of IDecoupledTemplateLogicResolver that you want to use can be easily configured in TemplateEngine:

final StandardDecoupledTemplateLogicResolver decoupledresolver = 
        new StandardDecoupledTemplateLogicResolver();
decoupledResolver.setPrefix("../viewlogic/");
...
templateEngine.setDecoupledTemplateLogicResolver(decoupledResolver);

Also popular now: