Table of contents17 Shared Template Logic
17.1. Shared Logic: Concept
Until now, we have worked in our Grocery Store with templates made in the usual way, with logic inserted into our templates as attributes.
But Thymeleaf also allows you to completely separate the template markup from the logic, allowing you to create completely logic-free markup templates in the modes of HTML and XML-templates.
')
The basic idea is that the template logic 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> <table id="usersTable"> <tr> <td class="username">Jeremy Grapefruit</td> <td class="usertype">Normal User</td> </tr> <tr> <td class="username">Alice Watermelon</td> <td class="usertype">Administrator</td> </tr> </table> </body> </html>
Absolutely no Thymeleaf code. This is a template file that a designer without Thymeleaf knowledge could create, edit and / or understand. Or HTML fragment provided by any 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> <attr sel="#usersTable" th:remove="all-but-first"> <attr sel="/tr[0]" th:each="user : ${users}"> <attr sel="td.username" th:text="${user.name}" /> <attr sel="td.usertype" th:text="#{|user.type.${user.type}|}" /> </attr> </attr> </thlogic>
Here we see a lot of
<attr> tags inside the
thlogic block. Those
<attr> tags insert attributes on the nodes of the original template, selected using their
sel attributes, which contain a Thymeleaf markup selector (actually
AttoParser markup
selectors ).
Also note that
<attr> tags can be nested so that their selectors are added. For example,
sel = "/ tr [0]" will be treated as
sel = "# usersTable / tr [0]" . The selector for the username
<td> will be processed as
sel = "# usersTable / tr [0] // td.username" .
Therefore, as soon as they are merged, both files, seen above, will be like this:
<!DOCTYPE html> <html> <body> <table id="usersTable" th:remove="all-but-first"> <tr th:each="user : ${users}"> <td class="username" th:text="${user.name}">Jeremy Grapefruit</td> <td class="usertype" th:text="#{|user.type.${user.type}|}">Normal User</td> </tr> <tr> <td class="username">Alice Watermelon</td> <td class="usertype">Administrator</td> </tr> </table> </body> </html>
It looks more familiar and more convenient than creating two separate files. But the advantage of separated patterns is that we can provide our templates with complete independence from Thymeleaf and, therefore, better maintainability from a design point of view.
Of course, contracts between designers or developers will be required, for example, the fact that
<table> users need
id = "usersTable" , but in many scenarios a template with pure HTML will be a much better communication artifact between development teams and designers.
17.2 Configuring Split Templates
Including Split TemplatesSplit logic will not be expected for each default template. Instead, customized resolvers for templates (ITemplateResolver implementations) will have to specifically label patterns they find using split 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 single resource:
final ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(servletContext); templateResolver.setUseDecoupledLogic(true);
Mixing Connected and Split LogicShared template 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 it exists. If there is no shared logical resource, the error will not be issued.
In addition, we can mix both connected and divided logic in the same template, for example, by adding some Thymeleaf attributes to the original template file, but leaving others for a separate shared logical file. The most common case for this is the use of the new ref attribute (in v3.0)
th: ref .
17.3 th attribute: ref
th: ref is just a marker attribute. It does nothing from the point of view of processing and simply disappears when processing a template, but its usefulness is that it acts as a reference to markup, that is, it can be resolved by name from the markup selector just like the tag name or fragment (
th: fragment ).
Therefore, if we have a selector type:
<attr sel="whatever" .../>
This will correspond to:
- Any <whatever> tag
- Any tag with th: fragment = "whatever" attribute
- Any tag with th: ref = "whatever" attribute
What is the advantage of
th: ref versus, for example, using the pure-HTML id attribute? Just the 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 can eventually pollute our output.
And yet, what is the disadvantage of
th: ref ? Obviously, we will add some Thymeleaf logic to our templates.
Note that the applicability of the
th: ref attribute is not only applicable to shared logical pattern files: it works the same in other types of scripts, for example, in fragment expressions
(~ {...}) .
17.4 Performance of Split Templates
The impact is very small. When the pattern found is flagged to use shared logic and it is not cached, the pattern 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 analyzed, and while it is being analyzed, these attributes will be injected on the fly by the parser itself, thanks to the advanced features of node selection 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 the 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 corresponding to 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 the default prefix will be empty, and the suffix will be .th.xml
- Second, it asks the template resource to find a relative resource with the calculated name using the ITemplateResource # relative (String relativeLocation) method
The specific
IDecoupledTemplateLogicResolver implementation you want to use can be easily configured in TemplateEngine:
final StandardDecoupledTemplateLogicResolver decoupledresolver = new StandardDecoupledTemplateLogicResolver(); decoupledResolver.setPrefix("../viewlogic/"); ... templateEngine.setDecoupledTemplateLogicResolver(decoupledResolver);