⬆️ ⬇️

Thymeleaf Tutorial: Chapter 4. Standard Expression Syntax

Table of contents



4 Standard Expression Syntax Syntax



We will take a short break in the development of our virtual grocery store to learn about one of the most important parts of the Standard Thymeleaf dialect: Thymeleaf Expression Syntax Standard .



We have already seen two types of valid attribute values ​​expressed in this syntax: messages and variables:

')

<p th:utext="#{home.welcome}">Welcome to our grocery store!</p> 


 <p>Today is: <span th:text="${today}">13 february 2011</span></p> 


But there are significantly more expressions. First, let's take a quick look at Standard Expression:





Expressions can be combined and nested :



 'User is of type ' + (${user.isAdmin()} ? 'Administrator' : (${user.type} ?: 'Unknown')) 


4.1 Messages



As we know, the message expression # {...} allows us to bind:



 <p th:utext="#{home.welcome}">Welcome to our grocery store!</p> 


… with:



 home.welcome=¡Bienvenido a nuestra tienda de comestibles! 


But there is one aspect that we haven’t thought of yet: what happens if the message text is not completely static? What if, for example, our application knows which user is visiting the site, and we would like to greet the user by name?



 <p>¡Bienvenido a nuestra tienda de comestibles, John Apricot!</p> 


This means that we need to add a parameter to our message. Like this:



 home.welcome=¡Bienvenido a nuestra tienda de comestibles, {0}! 


The parameter is defined relative to the standard java.text.MessageFormat syntax, which means that you can format numbers and dates.



To specify the value for our parameter from the HTTP attribute of the session, called by the user, we write:



 <p th:utext="#{home.welcome(${session.user.name})}"> Welcome to our grocery store, Sebastian Pepper! </p> 


Multiple parameters can be specified by separating them with commas. Also, the message keys themselves can come from a variable:



 <p th:utext="#{${welcomeMsgKey}(${session.user.name})}"> Welcome to our grocery store, Sebastian Pepper! </p> 


4.2 Variables



We have already mentioned that the $ {...} expressions are actually OGNL (Object-Graph Navigation Language) expressions executed on map variables contained in the context.



For more information on OGNL syntax and functions, you should read the OGNL Language Guide.



In Spring applications with MVC support, OGNL will be replaced with SpringEL, but its syntax is very similar to OGNL syntax (in fact, exactly the same for most common cases).



From the syntax of OGNL, we know the expression:



 <p>Today is: <span th:text="${today}">13 february 2011</span>.</p> 


equivalent to:



 ctx.getVariable("today"); 


But OGNL allows you to create more powerful expressions like this:



 <p th:utext="#{home.welcome(${session.user.name})}"> Welcome to our grocery store, Sebastian Pepper! </p> 


... get the username:



 ((User) ctx.getVariable("session").get("user")).getName(); 


But getter is just one of the features of OGNL. We'll see more:



 /* *       (.).   getter . */ ${person.father.name} /* *          ([])   *        . */ ${person['father']['name']} /* *    map,        *   get(...). */ ${countriesByCode.ES} ${personsByName['Stephen Zucchini'].age} /* *          , *     . */ ${personsArray[0].name} /* *       . */ ${person.createCompleteName()} ${person.createCompleteNameWithSeparator('-')} 


Expression Basic Objects



When executing an OGNL expression with context variables, some objects become available for greater flexibility. These objects can be referenced (according to the OGNL standard), starting with the # symbol:



#ctx : context.

#vars : context variables.

#locale : context locale.

#request : (only in Web Contexts) HttpServletRequest object.

#response : (Web Contexts only) HttpServletResponse object.

#session : (Web Contexts only) HttpSession object.

#servletContext : (only in Web Contexts) ServletContext object.



We can also do the following:



 <span th:text="${#locale.country}">US</span> 


You can read the full links to these objects in Appendix A.



Expression utilities



In addition to these basic objects, Thymeleaf offers us a set of useful objects that will help us to perform common tasks in our expressions.



#execInfo : information about the current template.

#messages : methods for receiving messages inside expressions, just as they will be obtained using the syntax # {...}.

#uris : methods for escaping URL / URI parts.

#conversions : methods for making conversion service settings (if present).

#dates : methods for java.util.Date objects: formatting, extracting components, and so on.

#calendars : similar to #dates, but for java.util.Calendar objects.

#numbers : methods for formatting numeric objects.

#strings : methods for String object: contains, startsWith, prepending / appending, and so on.

#objects : general methods for objects.

#bools : methods for boolean transformations.

#arrays : methods for arrays.

#lists : methods for List.

#sets : methods for Sets.

#maps : methods for Maps.

#aggregates : methods for aggregating masses or collections.

#ids : methods for handling id identifiers that can be repeated (for example, as a result of an iteration).



You can check which features are offered for each of these objects in Appendix B.



Reformatting dates on the Home page



We now know about these utility objects and can use them to change the way that dates are displayed on our home page. Instead, in our HomeController:



 SimpleDateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy"); Calendar cal = Calendar.getInstance(); WebContext ctx = new WebContext(request, servletContext, request.getLocale()); ctx.setVariable("today", dateFormat.format(cal.getTime())); templateEngine.process("home", ctx, response.getWriter()); 


... we can do the following:



 WebContext ctx = new WebContext(request, response, servletContext, request.getLocale()); ctx.setVariable("today", Calendar.getInstance()); templateEngine.process("home", ctx, response.getWriter()); 


... and then format the date in the presentation layer itself:



 <p> Today is: <span th:text="${#calendars.format(today,'dd MMMM yyyy')}">13 May 2011</span> </p> 


4.3 Selection expressions / selections (asterisk syntax)



Expressions with variables can be written not only through $ {...} , but also as * {...} .



There is a difference between the variants: the syntax with an asterisk transforms the expression over the selected object rather than the entire context. This means that if there is no object selected, the dollar and asterisk do the same thing.



What is the selected object? The result of the expression using the " th: object " attribute. Use it on the profile page (userprofile.html):



 <div th:object="${session.user}"> <p>Name: <span th:text="*{firstName}">Sebastian</span>.</p> <p>Surname: <span th:text="*{lastName}">Pepper</span>.</p> <p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p> </div> 


Which is equivalent to:



 <div> <p>Name: <span th:text="${session.user.firstName}">Sebastian</span>.</p> <p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p> <p>Nationality: <span th:text="${session.user.nationality}">Saturn</span>.</p> </div> 


Of course, dollar and asterisk can be mixed:



 <div th:object="${session.user}"> <p>Name: <span th:text="*{firstName}">Sebastian</span>.</p> <p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p> <p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p> </div> 


When the selected object is in place, the selected object will be as accessible with the dollar as the #object variable:



 <div th:object="${session.user}"> <p>Name: <span th:text="${#object.firstName}">Sebastian</span>.</p> <p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p> <p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p> </div> 


As stated, if there is no object selected, the dollar and asterisk are equivalent.



 <div> <p>Name: <span th:text="*{session.user.name}">Sebastian</span>.</p> <p>Surname: <span th:text="*{session.user.surname}">Pepper</span>.</p> <p>Nationality: <span th:text="*{session.user.nationality}">Saturn</span>.</p> </div> 


4.4 Link URL



Because of their importance, URLs are first-class "citizens" in web application templates, and the Standard Thymeleaf dialect has a special "@" syntax for them: @ {...}



There are different types of URLs:





The actual processing of these expressions and their conversion into URLs that will be output are performed using the implementation of the org.thymeleaf.linkbuilder.ILinkBuilder interface, which are registered in the used ITemplateEngine object.



By default, one implementation of this interface is represented by the org.thymeleaf.linkbuilder.StandardLinkBuilder class, which is sufficient for both stand-alone (not web sites) and web-based scripts based on the Servlet API. Other scenarios (for example, integration with non-ServletAPI web frameworks) may require specific implementations of the link builder interface.



Let's use the new syntax. Meet the attribute " th: href ":



 <!--  'http://localhost:8080/gtvg/order/details?orderId=3' ( ) --> <a href="details.html" th:href="@{http://localhost:8080/gtvg/order/details(orderId=${o.id})}">view</a> <!--  '/gtvg/order/details?orderId=3' ( ) --> <a href="details.html" th:href="@{/order/details(orderId=${o.id})}">view</a> <!--  '/gtvg/order/3/details' ( ) --> <a href="details.html" th:href="@{/order/{orderId}/details(orderId=${o.id})}">view</a> 


A few notes:





As with the message syntax (# {...}), a URL can also be the result of a different expression:



 <a th:href="@{${url}(orderId=${o.id})}">view</a> <a th:href="@{'/details/'+${user.login}(orderId=${o.id})}">view</a> 


Menu for our home page



Now we know how to create URLs, what about adding a small navigation menu to our Home page?



 <p>Please select an option</p> <ol> <li><a href="product/list.html" th:href="@{/product/list}">Product List</a></li> <li><a href="order/list.html" th:href="@{/order/list}">Order List</a></li> <li><a href="subscribe.html" th:href="@{/subscribe}">Subscribe to our Newsletter</a></li> <li><a href="userprofile.html" th:href="@{/userprofile}">See User Profile</a></li> </ol> 


Links URLs relative to server root directory



Additional syntax can be used to create URLs based on the root directory (instead of context-sensitive) URLs to refer to different contexts on the same server. These URLs will be listed as @ {~ / path / to / something}



4.5 Fragments / Fragments



Fragment expressions are an easy way to represent markup fragments and move them between templates. This allows us to copy, transfer them to other templates as arguments and so on.



The most common use is to insert a fragment using th: insert or th: replace (more on this in the next section):



<div th: insert = "~ {commons :: main}"> ...



But they can be used everywhere, like any other variable:



 <div th:with="frag=~{footer :: #main/text()}"> <p th:insert="${frag}"> </div> 


Later in this tutorial, there is a whole section on Template Layout , including a deeper explanation of fragment expressions.



4.6 Literals / Literals



Text literals



Text literals are just character strings between single quotes. They can include any character, but you must avoid any single quotes inside them using \ ' .



 <p> Now you are looking at a <span th:text="'working web application'">template file</span>. </p> 


Numeric Literals



Numeric literals are just numbers.



 <p>The year is <span th:text="2013">1492</span>.</p> <p>In two years, it will be <span th:text="2013 + 2">1494</span>.</p> 


Boolean / Boolean Literals



Boolean literals are true and false. For example:



 <div th:if="${user.isAdmin()} == false"> ... 


In this example, "== false" is written outside the brackets, then Thymeleaf takes care of the expression. If equality were contained inside parentheses, the OGNL / SpringEL engine would take care of it:



 <div th:if="${user.isAdmin() == false}"> ... 


Literal null



The literal null can also be used:



 <div th:if="${variable.something} == null"> ... 


Literal Tokens



Numeric, Boolean, and null literals are a special case of literal tokens.



These tokens allow you to slightly simplify Standard Expressions. They work exactly like text literals ('...'), but only allow letters (AZ and az), numbers (0-9), brackets ([and]), periods (.), Hyphens (-) and underscores (_). No spaces, no commas, and so on.



The good part? Tokens do not need quotes surrounding them . Therefore, we can do:



 <div th:class="content">...</div> 


instead:



 <div th:class="'content'">...</div> 


4.7 Appending texts



Texts, regardless of whether they are literals or the result of processing variables or messages, can be easily added using the + operator:



 <span th:text="'The name of the user is ' + ${user.name}"> 


4.8 Replacement Literals



Literal replacements make it easy to format strings containing values ​​from variables, without the need to add literals using '...' + '...'.



These replacements should be surrounded by vertical bars (|) , for example:



 <span th:text="|Welcome to our application, ${user.name}!|"> 


Which is equivalent to:



 <span th:text="'Welcome to our application, ' + ${user.name} + '!'"> 


Literal replacements can be combined with other types of expressions:



 <span th:text="${onevar} + ' ' + |${twovar}, ${threevar}|"> 


Only variable / message expressions ($ {...}, * {...}, # {...}) are allowed inside | ... | literal replacements. No other literals ('...'), boolean / numeric tokens, conditional expressions, and so on.



4.9 Arithmetic operations



Some arithmetic operations are also available: +, -, *, /,% .



 <div th:with="isEven=(${prodStat.count} % 2 == 0)"> 


Note that these operators can also be applied directly to the OGNL expressions themselves (in this case, OGNL will be executed instead of the Thymeleaf Standard Expression mechanism):



 <div th:with="isEven=${prodStat.count % 2 == 0}"> 


Please note that for some of these operators there are text aliases: div (/), mod (%).



4.10 Comparisons and Equality



Values ​​in expressions can be compared with the characters >, <,> = and <=, ==,! = Operators are used to check equality (or lack thereof). Note that XML sets <and> characters should not be used in attribute values, and therefore must be replaced with & lt; and & gt;



 <div th:if="${prodStat.count} > 1"> 


 <span th:text="'Execution mode is ' + ( (${execMode} == 'dev')? 'Development' : 'Production')"> 


A simpler alternative could be to use text aliases that exist for some of these operators: gt (>), lt (<), ge (> =), le (<=), not (!). Also eq (==), neq / ne (! =).



4.11 Conditional Expressions



Conditional expressions are designed to execute only one of the two expressions depending on the result of the evaluation of the condition (which is itself another expression).



Let's look at an example fragment (introducing another attribute modifier, th: class ):



 <tr th:class="${row.even}? 'even' : 'odd'"> ... </tr> 


All three parts of the conditional expression (condition, then, and else) are themselves expressions, which means they can be variables ( $ {...} , * {...} ), messages ( # {...} ), URLs ( @ {...} ) or literals ( '...' ).



Conditional expressions can also be nested with parentheses:



 <tr th:class="${row.even}? (${row.first}? 'first' : 'even') : 'odd'"> ... </tr> 


Other expressions may also be omitted, in which case a null value is returned if the condition is false:



 <tr th:class="${row.even}? 'alt'"> ... </tr> 


4.12 Default / Default Expression (Elvis statement)



The default expression is a special kind of conditional value without any part. This is equivalent to the Elvis operator present in some languages, for example Groovy, which allows you to specify two expressions: the first is used if it is not null, but if so, the second is used.



Let's see how it works on our profile page:



 <div th:object="${session.user}"> ... <p>Age: <span th:text="*{age}?: '(no age specified)'">27</span>.</p> </div> 


As you can see, the operator is?:, And we use it here to specify the default value for the name (the literal value in this case) only if the result of the calculation * {age} is null. Therefore, it is equivalent to:



 <p>Age: <span th:text="*{age != null}? *{age} : '(no age specified)'">27</span>.</p> 


Like conditional values, they can contain nested expressions between parentheses:



 <p> Name: <span th:text="*{firstName}?: (*{admin}? 'Admin' : #{default.username})">Sebastian</span> </p> 


4.13 No-Operation Token





The No-Operation Token is represented by a (_) character.



The idea of ​​this token is to indicate that the desired result for an expression is to do nothing: do it exactly as if the processed attribute (for example, th: text) was completely absent.



Among other features, this allows developers to use prototype text as default values. For example, instead of:



 <span th:text="${user.name} ?: 'no user authenticated'">...</span> 


... we can directly use 'no user authenticated' as prototyping text, which makes the code more concise and universal in terms of design:



 <span th:text="${user.name} ?: _">no user authenticated</span> 


4.15 Data Transformation / Formatting



Thymeleaf defines the syntax of double brackets for variables ( $ {...} ) and selected ( * {...} ) expressions, which allows us to apply data transformation using configuration of the transformation service.



Base it looks like this:



 <td th:text="${{user.lastAccessDate}}">...</td> 


Have you noticed a double bracket ?: $ {{...}} . This instructs Thymeleaf to pass the result of the user.lastAccessDate expression to the translation service and asks you to perform a formatting operation (conversion to String) before returning the result.



Assuming user.lastAccessDate is of type java.util.Calendar, if the translation service (implementation of IStandardConversionService) has been registered and contains a valid conversion for Calendar -> String, it will be used.



By default, the implementation of the IStandardConversionService (StandardConversionService class) simply executes .toString () for any object. For more information about how to register a custom implementation of a conversion service, see the "Additional Configuration Information" section.



The official thymeleaf-spring3 and thymeleaf-spring4 integration packages transparently integrate the Thymeleaf conversion service engine with its own Conversion Service Spring infrastructure, so that the conversion services and formats announced in the Spring configuration will be automatically available for the $ {{...}} and * expressions {{...}}.



4.14 Preprocessing



In addition to all these functions for processing expressions, Thymeleaf has a preprocessor expression function.



Preprocessing is the execution of expressions made before the execution of standard expressions, which allows modifying an expression that will eventually be executed.



Pre-processed expressions are exactly the same as normal, but appear in the environment of a double underscore character (for example, __ $ {expression} __).



Suppose we have an i18n Messages_fr.properties entry that contains an OGNL expression that calls a static language-specific method, for example:



 article.text=@myapp.translator.Translator@translateToFrench({0}) 


... and the equivalent of Messages_es.properties:



 article.text=@myapp.translator.Translator@translateToSpanish({0}) 


We can create a markup fragment that performs one expression or another depending on the locale. To do this, we first select the expression (by preprocessing), and then start Thymeleaf:



 <p th:text="${__#{article.text('textVar')}__}">Some text here...</p> 


Please note that the preprocessing step for the French language will create the following equivalent:



 <p th:text="${@myapp.translator.Translator@translateToFrench(textVar)}">Some text here...</p> 


The preprocessor string __ can be escaped in attributes with \ _ \ _ .

Source: https://habr.com/ru/post/350870/



All Articles