--- title: 'Tutorial: Using Thymeleaf' author: Thymeleaf version: @documentVersion@ thymeleafVersion: @projectVersion@ --- 1 Introducing Thymeleaf ======================= 1.1 What is Thymeleaf? ---------------------- Thymeleaf is a modern server-side Java template engine for both web and standalone environments, capable of processing HTML, XML, JavaScript, CSS and even plain text. The main goal of Thymeleaf is to provide an elegant and highly-maintainable way of creating templates. To achieve this, it builds on the concept of *Natural Templates* to inject its logic into template files in a way that doesn't affect the template from being used as a design prototype. This improves communication of design and bridges the gap between design and development teams. Thymeleaf has also been designed from the beginning with Web Standards in mind -- especially **HTML5** -- allowing you to create fully validating templates if that is a need for you. 1.2 What kind of templates can Thymeleaf process? ------------------------------------------------- Out-of-the-box, Thymeleaf allows you to process six kinds of templates, each of which is called a **Template Mode**: * HTML * XML * TEXT * JAVASCRIPT * CSS * RAW There are two *markup* template modes (`HTML` and `XML`), three *textual* template modes (`TEXT`, `JAVASCRIPT` and `CSS`) and a *no-op* template mode (`RAW`). The **`HTML`** template mode will allow any kind of HTML input, including HTML5, HTML 4 and XHTML. No validation or well-formedness check will be performed, and template code/structure will be respected to the biggest possible extent in output. The **`XML`** template mode will allow XML input. In this case, code is expected to be well-formed -- no unclosed tags, no unquoted attributes, etc -- and the parser will throw exceptions if well-formedness violations are found. Note that no *validation* (against a DTD or XML Schema) will be performed. The **`TEXT`** template mode will allow the use of a special syntax for templates of a non-markup nature. Examples of such templates might be text emails or templated documentation. Note that HTML or XML templates can be also processed as `TEXT`, in which case they will not be parsed as markup, and every tag, DOCTYPE, comment, etc, will be treated as mere text. The **`JAVASCRIPT`** template mode will allow the processing of JavaScript files in a Thymeleaf application. This means being able to use model data inside JavaScript files in the same way it can be done in HTML files, but with JavaScript-specific integrations such as specialized escaping or *natural scripting*. The `JAVASCRIPT` template mode is considered a *textual* mode and therefore uses the same special syntax as the `TEXT` template mode. The **`CSS`** template mode will allow the processing of CSS files involved in a Thymeleaf application. Similar to the `JAVASCRIPT` mode, the `CSS` template mode is also a *textual* mode and uses the special processing syntax from the `TEXT` template mode. The **`RAW`** template mode will simply not process templates at all. It is meant to be used for inserting untouched resources (files, URL responses, etc.) into the templates being processed. For example, external, uncontrolled resources in HTML format could be included into application templates, safely knowing that any Thymeleaf code that these resources might include will not be executed. 1.3 Dialects: The Standard Dialect ---------------------------------- Thymeleaf is an extremely extensible template engine (in fact it could be called a _template engine framework_) that allows you to define and customize the way your templates will be processed to a fine level of detail. An object that applies some logic to a markup artifact (a tag, some text, a comment, or a mere placeholder if templates are not markup) is called a _processor_, and a set of these processors -- plus perhaps some extra artifacts -- is what a **dialect** is normally comprised of. Out of the box, Thymeleaf's core library provides a dialect called the **Standard Dialect**, which should be enough for most users. > Note that dialects can actually have no processors and be entirely comprised > of other kinds of artifacts, but processors are definitely the most common > use case. _This tutorial covers the Standard Dialect_. Every attribute and syntax feature you will learn about in the following pages is defined by this dialect, even if that isn't explicitly mentioned. Of course, users can create their own dialects (even extending the Standard one) if they want to define their own processing logic while taking advantage of the library's advanced features. Thymeleaf can also be configured to use several dialects at a time. > The official thymeleaf-spring3 and thymeleaf-spring4 integration packages > both define a dialect called the "SpringStandard Dialect", which is mostly the > same as the Standard Dialect, but with small adaptations to make better use of > some features in the Spring Framework (for example, by using Spring Expression > Language or SpringEL instead of OGNL). So if you are a Spring MVC user you are > not wasting your time, as almost everything you learn here will be of use in > your Spring applications. Most of the processors of the Standard Dialect are _attribute processors_. This allows browsers to correctly display HTML template files even before being processed because they will simply ignore the additional attributes. For example, while a JSP using tag libraries could include a fragment of code not directly displayable by a browser like: ```html ``` ...the Thymeleaf Standard Dialect would allow us to achieve the same functionality with: ```html ``` Not only will this be correctly displayed by browsers, but this also allows us to (optionally) specify a value attribute in it ("James Carrot", in this case) that will be displayed when the prototype is statically opened in a browser, and that will be substituted by the value resulting from the evaluation of `${user.name}` during processing of the template. This helps your designer and developer to work on the very same template file and reduce the effort required to transform a static prototype into a working template file. The ability to do this is a feature called _Natural Templating_. 2 The Good Thymes Virtual Grocery ================================= The source code for the examples shown in this, and future chapters of this guide, can be found in the _Good Thymes Virtual Grocery (GTVG)_ example app which has two (equivalent) versions: * `javax.*` based: [gtvg-javax](https://github.com/thymeleaf/thymeleaf/tree/3.1-master/examples/core/thymeleaf-examples-gtvg-javax). * `jakarta.*` based: [gtvg-jakarta](https://github.com/thymeleaf/thymeleaf/tree/3.1-master/examples/core/thymeleaf-examples-gtvg-jakarta). 2.1 A website for a grocery --------------------------- To better explain the concepts involved in processing templates with Thymeleaf, this tutorial will use a demo application which you can download from the project's web site. This application is the web site of an imaginary virtual grocery, and will provide us with many scenarios to showcase Thymeleaf's many features. To start, we need a simple set of model entities for our application: `Products` which are sold to `Customers` by creating `Orders`. We will also be managing `Comments` about those `Products`: ![Example application model](images/usingthymeleaf/gtvg-model.png) Our application will also have a very simple service layer, composed by `Service` objects containing methods like: ```java public class ProductService { ... public List findAll() { return ProductRepository.getInstance().findAll(); } public Product findById(Integer id) { return ProductRepository.getInstance().findById(id); } } ``` At the web layer our application will have a filter that will delegate execution to Thymeleaf-enabled commands depending on the request URL: ```java /* * The application object needs to be declared first (implements IWebApplication) * In this case, the Jakarta-based version will be used. */ public void init(final FilterConfig filterConfig) throws ServletException { this.application = JakartaServletWebApplication.buildApplication( filterConfig.getServletContext()); // We will see later how the TemplateEngine object is built and configured this.templateEngine = buildTemplateEngine(this.application); } /* * Each request will be processed by creating an exchange object (modeling * the request, its response and all the data needed for this process) and * then calling the corresponding controller. */ private boolean process(HttpServletRequest request, HttpServletResponse response) throws ServletException { try { final IWebExchange webExchange = this.application.buildExchange(request, response); final IWebRequest webRequest = webExchange.getRequest(); // This prevents triggering engine executions for resource URLs if (request.getRequestURI().startsWith("/css") || request.getRequestURI().startsWith("/images") || request.getRequestURI().startsWith("/favicon")) { return false; } /* * Query controller/URL mapping and obtain the controller * that will process the request. If no controller is available, * return false and let other filters/servlets process the request. */ final IGTVGController controller = ControllerMappings.resolveControllerForRequest(webRequest); if (controller == null) { return false; } /* * Write the response headers */ response.setContentType("text/html;charset=UTF-8"); response.setHeader("Pragma", "no-cache"); response.setHeader("Cache-Control", "no-cache"); response.setDateHeader("Expires", 0); /* * Obtain the response writer */ final Writer writer = response.getWriter(); /* * Execute the controller and process view template, * writing the results to the response writer. */ controller.process(webExchange, this.templateEngine, writer); return true; } catch (Exception e) { try { response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } catch (final IOException ignored) { // Just ignore this } throw new ServletException(e); } } ``` This is our `IGTVGController` interface: ```java public interface IGTVGController { public void process( final IWebExchange webExchange, final ITemplateEngine templateEngine, final Writer writer) throws Exception; } ``` All we have to do now is create implementations of the `IGTVGController` interface, retrieving data from the services and processing templates using the `ITemplateEngine` object. In the end, it will look like this: ![Example application home page](images/usingthymeleaf/gtvg-view.png) But first let's see how that template engine is initialized. 2.2 Creating and configuring the Template Engine ------------------------------------------------ The _init(...)_ method in our filter contained this line: ```java this.templateEngine = buildTemplateEngine(this.application); ``` Let's see now how our `org.thymeleaf.TemplateEngine` object is initialized: ```java private static ITemplateEngine buildTemplateEngine(final IWebApplication application) { // Templates will be resolved as application (ServletContext) resources final WebApplicationTemplateResolver templateResolver = new WebApplicationTemplateResolver(application); // HTML is the default mode, but we will set it anyway for better understanding of code templateResolver.setTemplateMode(TemplateMode.HTML); // This will convert "home" to "/WEB-INF/templates/home.html" templateResolver.setPrefix("/WEB-INF/templates/"); templateResolver.setSuffix(".html"); // Set template cache TTL to 1 hour. If not set, entries would live in cache until expelled by LRU templateResolver.setCacheTTLMs(Long.valueOf(3600000L)); // Cache is set to true by default. Set to false if you want templates to // be automatically updated when modified. templateResolver.setCacheable(true); final TemplateEngine templateEngine = new TemplateEngine(); templateEngine.setTemplateResolver(templateResolver); return templateEngine; } ``` There are many ways of configuring a `TemplateEngine` object, but for now these few lines of code will teach us enough about the steps needed. ### The Template Resolver Let's start with the Template Resolver: ```java final WebApplicationTemplateResolver templateResolver = new WebApplicationTemplateResolver(application); ``` Template Resolvers are objects that implement an interface from the Thymeleaf API called `org.thymeleaf.templateresolver.ITemplateResolver`: ```java public interface ITemplateResolver { ... /* * Templates are resolved by their name (or content) and also (optionally) their * owner template in case we are trying to resolve a fragment for another template. * Will return null if template cannot be handled by this template resolver. */ public TemplateResolution resolveTemplate( final IEngineConfiguration configuration, final String ownerTemplate, final String template, final Map templateResolutionAttributes); } ``` These objects are in charge of determining how our templates will be accessed and, in this GTVG application, the use of `org.thymeleaf.templateresolver.WebApplicationTemplateResolver` means that we are going to retrieve our template files as resources from the _IWebApplication_ object: a Thymeleaf abstraction that, in Servlet-based applications, basically wraps around Servlet API's `[javax|jakarta].servlet.ServletContext` object, and that resolves resources from the web application root. But that's not all we can say about the template resolver, because we can set some configuration parameters on it. First, the template mode: ```java templateResolver.setTemplateMode(TemplateMode.HTML); ``` HTML is the default template mode for `WebApplicationTemplateResolver`, but it is good practice to establish it anyway so that our code documents clearly what is going on. ```java templateResolver.setPrefix("/WEB-INF/templates/"); templateResolver.setSuffix(".html"); ``` The _prefix_ and _suffix_ modify the template names that we will be passing to the engine for obtaining the real resource names to be used. Using this configuration, the template name _"product/list"_ would correspond to: ```java servletContext.getResourceAsStream("/WEB-INF/templates/product/list.html") ``` Optionally, the amount of time that a parsed template can live in the cache is configured at the Template Resolver by means of the _cacheTTLMs_ property: ```java templateResolver.setCacheTTLMs(3600000L); ``` A template can still be expelled from cache before that TTL is reached if the max cache size is reached and it is the oldest entry currently cached. > Cache behaviour and sizes can be defined by the user by implementing the `ICacheManager` > interface or by modifying the `StandardCacheManager` object to manage the > default cache. There is much more to learn about template resolvers, but for now let's have a look at the creation of our Template Engine object. ### The Template Engine Template Engine objects are implementations of the `org.thymeleaf.ITemplateEngine` interface. One of these implementations is offered by the Thymeleaf core: `org.thymeleaf.TemplateEngine`, and we create an instance of it here: ```java templateEngine = new TemplateEngine(); templateEngine.setTemplateResolver(templateResolver); ``` Rather simple, isn't it? All we need is to create an instance and set the Template Resolver to it. A template resolver is the only *required* parameter a `TemplateEngine` needs, although there are many others that will be covered later (message resolvers, cache sizes, etc). For now, this is all we need. Our Template Engine is now ready and we can start creating our pages using Thymeleaf. 3 Using Texts ============= 3.1 A multi-language welcome ---------------------------- Our first task will be to create a home page for our grocery site. The first version of this page will be extremely simple: just a title and a welcome message. This is our `/WEB-INF/templates/home.html` file: ```html Good Thymes Virtual Grocery

Welcome to our grocery store!

``` The first thing you will notice is that this file is HTML5 that can be correctly displayed by any browser because it does not include any non-HTML tags (browsers ignore all attributes they don't understand, like `th:text`). But you may also notice that this template is not really a _valid_ HTML5 document, because these non-standard attributes we are using in the `th:*` form are not allowed by the HTML5 specification. In fact, we are even adding an `xmlns:th` attribute to our `` tag, something absolutely non-HTML5-ish: ```html ``` ...which has no influence at all in template processing, but works as an *incantation* that prevents our IDE from complaining about the lack of a namespace definition for all those `th:*` attributes. So what if we wanted to make this template **HTML5-valid**? Easy: switch to Thymeleaf's data attribute syntax, using the `data-` prefix for attribute names and hyphen (`-`) separators instead of semi-colons (`:`): ```html Good Thymes Virtual Grocery

Welcome to our grocery store!

``` Custom `data-` prefixed attributes are allowed by the HTML5 specification, so, with this code above, our template would be a *valid HTML5 document*. > Both notations are completely equivalent and interchangeable, but for the sake > of simplicity and compactness of the code samples, this tutorial will use the > *namespace notation* (`th:*`). Also, the `th:*` notation is more general and > allowed in every Thymeleaf template mode (`XML`, `TEXT`...) whereas the `data-` > notation is only allowed in `HTML` mode. ### Using th:text and externalizing text Externalizing text is extracting fragments of template code out of template files so that they can be kept in separate files (typically `.properties` files) and that they can be easily replaced with equivalent texts written in other languages (a process called internationalization or simply _i18n_). Externalized fragments of text are usually called *"messages"*. Messages always have a key that identifies them, and Thymeleaf allows you to specify that a text should correspond to a specific message with the `#{...}` syntax: ```html

Welcome to our grocery store!

``` What we can see here are in fact two different features of the Thymeleaf Standard Dialect: * The `th:text` attribute, which evaluates its value expression and sets the result as the body of the host tag, effectively replacing the "Welcome to our grocery store!" text we see in the code. * The `#{home.welcome}` expression, specified in the _Standard Expression Syntax_, instructing that the text to be used by the `th:text` attribute should be the message with the `home.welcome` key corresponding to whichever locale we are processing the template with. Now, where is this externalized text? The location of externalized text in Thymeleaf is fully configurable, and it will depend on the specific `org.thymeleaf.messageresolver.IMessageResolver` implementation being used. Normally, an implementation based on `.properties` files will be used, but we could create our own implementations if we wanted, for example, to obtain messages from a database. However, we have not specified a message resolver for our template engine during initialization, and that means that our application is using the _Standard Message Resolver_, implemented by `org.thymeleaf.messageresolver.StandardMessageResolver`. The standard message resolver expects to find messages for `/WEB-INF/templates/home.html` in properties files in the same folder and with the same name as the template, like: * `/WEB-INF/templates/home_en.properties` for English texts. * `/WEB-INF/templates/home_es.properties` for Spanish language texts. * `/WEB-INF/templates/home_pt_BR.properties` for Portuguese (Brazil) language texts. * `/WEB-INF/templates/home.properties` for default texts (if the locale is not matched). Let's have a look at our `home_es.properties` file: ``` home.welcome=¡Bienvenido a nuestra tienda de comestibles! ``` This is all we need for making Thymeleaf process our template. Let's create our Home controller then. ### Contexts In order to process our template, we will create a `HomeController` class implementing the `IGTVGController` interface we saw before: ```java public class HomeController implements IGTVGController { public void process( final IWebExchange webExchange, final ITemplateEngine templateEngine, final Writer writer) throws Exception { WebContext ctx = new WebContext(webExchange, webExchange.getLocale()); templateEngine.process("home", ctx, writer); } } ``` The first thing we see is the creation of a *context*. A Thymeleaf context is an object implementing the `org.thymeleaf.context.IContext` interface. Contexts should contain all the data required for an execution of the template engine in a variables map, and also reference the locale that must be used for externalized messages. ```java public interface IContext { public Locale getLocale(); public boolean containsVariable(final String name); public Set getVariableNames(); public Object getVariable(final String name); } ``` There is a specialized extension of this interface, `org.thymeleaf.context.IWebContext`, meant to be used in web applications. ```java public interface IWebContext extends IContext { public IWebExchange getExchange(); } ``` The Thymeleaf core library offers an implementation of each of these interfaces: * `org.thymeleaf.context.Context` implements `IContext` * `org.thymeleaf.context.WebContext` implements `IWebContext` And as you can see in the controller code, `WebContext` is the one we use. In fact we have to, because the use of a `WebApplicationTemplateResolver` requires that we use a context implementing `IWebContext`. ```java WebContext ctx = new WebContext(webExchange, webExchange.getLocale()); ``` The `WebContext` constructor requires information contained in the `IWebExchange` abstraction object that was created at the filter representing this web-based interchange (i.e. request + response). The default locale of the system will be used if none is specified (although you should never let this happen in real applications). There are some specialized expressions that we will be able to use to obtain the request parameters and the request, session and application attributes from the `WebContext` in our templates. For example: * `${x}` will return a variable `x` stored into the Thymeleaf context or as an *exchange attribute* (A *"request attribute"* in Servlet jargon). * `${param.x}` will return a *request parameter* called `x` (which might be multivalued). * `${session.x}` will return a *session attribute* called `x`. * `${application.x}` will return an *application attribute* called `x` (a *"servlet context attribute"* in Servlet jargon). ### Executing the template engine With our context object ready, now we can tell the template engine to process the template (by its name) using the context, and passing it a response writer so that the response can be written to it: ```java templateEngine.process("home", ctx, writer); ``` Let's see the results of this using the Spanish locale: ```html Good Thymes Virtual Grocery

¡Bienvenido a nuestra tienda de comestibles!

``` 3.2 More on texts and variables ------------------------------- ### Unescaped Text The simplest version of our Home page seems to be ready now, but there is something we have not thought about... what if we had a message like this? ```java home.welcome=Welcome to our fantastic grocery store! ``` If we execute this template like before, we will obtain: ```html

Welcome to our <b>fantastic</b> grocery store!

``` Which is not exactly what we expected, because our `` tag has been escaped and therefore it will be displayed in the browser. This is the default behaviour of the `th:text` attribute. If we want Thymeleaf to respect our HTML tags and not escape them, we will have to use a different attribute: `th:utext` (for "unescaped text"): ```html

Welcome to our grocery store!

``` This will output our message just like we wanted it: ```html

Welcome to our fantastic grocery store!

``` ### Using and displaying variables Now let's add some more content to our home page. For example, we may want to display the date below our welcome message, like this: ``` Welcome to our fantastic grocery store! Today is: 12 july 2010 ``` First of all, we will have to modify our controller so that we add that date as a context variable: ```java public void process( final IWebExchange webExchange, final ITemplateEngine templateEngine, final Writer writer) throws Exception { SimpleDateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy"); Calendar cal = Calendar.getInstance(); WebContext ctx = new WebContext(webExchange, webExchange.getLocale()); ctx.setVariable("today", dateFormat.format(cal.getTime())); templateEngine.process("home", ctx, writer); } ``` We have added a `String` variable called `today` to our context, and now we can display it in our template: ```html

Welcome to our grocery store!

Today is: 13 February 2011

``` As you can see, we are still using the `th:text` attribute for the job (and that's correct, because we want to replace the tag's body), but the syntax is a little bit different this time and instead of a `#{...}` expression value, we are using a `${...}` one. This is a **variable expression**, and it contains an expression in a language called _OGNL (Object-Graph Navigation Language)_ that will be executed on the context variables map we talked about before. The `${today}` expression simply means "get the variable called today", but these expressions could be more complex (like `${user.name}` for "get the variable called user, and call its `getName()` method"). There are quite a lot of possibilities in attribute values: messages, variable expressions... and quite a lot more. The next chapter will show us what all these possibilities are. 4 Standard Expression Syntax ============================ We will take a small break in the development of our grocery virtual store to learn about one of the most important parts of the Thymeleaf Standard Dialect: the Thymeleaf Standard Expression syntax. We have already seen two types of valid attribute values expressed in this syntax: message and variable expressions: ```html

Welcome to our grocery store!

Today is: 13 february 2011

``` But there are more types of expressions, and more interesting details to learn about the ones we already know. First, let's see a quick summary of the Standard Expression features: * Simple expressions: * Variable Expressions: `${...}` * Selection Variable Expressions: `*{...}` * Message Expressions: `#{...}` * Link URL Expressions: `@{...}` * Fragment Expressions: `~{...}` * Literals * Text literals: `'one text'`, `'Another one!'`,... * Number literals: `0`, `34`, `3.0`, `12.3`,... * Boolean literals: `true`, `false` * Null literal: `null` * Literal tokens: `one`, `sometext`, `main`,... * Text operations: * String concatenation: `+` * Literal substitutions: `|The name is ${name}|` * Arithmetic operations: * Binary operators: `+`, `-`, `*`, `/`, `%` * Minus sign (unary operator): `-` * Boolean operations: * Binary operators: `and`, `or` * Boolean negation (unary operator): `!`, `not` * Comparisons and equality: * Comparators: `>`, `<`, `>=`, `<=` (`gt`, `lt`, `ge`, `le`) * Equality operators: `==`, `!=` (`eq`, `ne`) * Conditional operators: * If-then: `(if) ? (then)` * If-then-else: `(if) ? (then) : (else)` * Default: `(value) ?: (defaultvalue)` * Special tokens: * No-Operation: `_` All these features can be combined and nested: ```html 'User is of type ' + (${user.isAdmin()} ? 'Administrator' : (${user.type} ?: 'Unknown')) ``` 4.1 Messages ------------ As we already know, `#{...}` message expressions allow us to link this: ```html

Welcome to our grocery store!

``` ...to this: ``` home.welcome=¡Bienvenido a nuestra tienda de comestibles! ``` But there's one aspect we still haven't thought of: what happens if the message text is not completely static? What if, for example, our application knew who is the user visiting the site at any moment and we wanted to greet them by name? ```html

¡Bienvenido a nuestra tienda de comestibles, John Apricot!

``` This means we would need to add a parameter to our message. Just like this: ``` home.welcome=¡Bienvenido a nuestra tienda de comestibles, {0}! ``` Parameters are specified according to the [`java.text.MessageFormat`](https://docs.oracle.com/javase/10/docs/api/java/text/MessageFormat.html) standard syntax, which means you can format to numbers and dates as specified in the API docs for classes in the `java.text.*` package. In order to specify a value for our parameter, and given an HTTP session attribute called `user`, we could have: ```html

Welcome to our grocery store, Sebastian Pepper!

``` > Note that the use of `th:utext` here means that the formatted message will > not be escaped. This example assumes that `user.name` is already escaped. Several parameters can be specified, separated by commas. The message key itself can come from a variable: ```html

Welcome to our grocery store, Sebastian Pepper!

``` 4.2 Variables ------------- We already mentioned that `${...}` expressions are in fact OGNL (Object-Graph Navigation Language) expressions executed on the map of variables contained in the context. > For detailed info about OGNL syntax and features, you should read the > [OGNL Language Guide](http://commons.apache.org/ognl/) > > In Spring MVC-enabled applications OGNL will be replaced with **SpringEL**, > but its syntax is very similar to that of OGNL (actually, exactly the same for > most common cases). From OGNL's syntax, we know that the expression in: ```html

Today is: 13 february 2011.

``` ...is in fact equivalent to this: ```java ctx.getVariable("today"); ``` But OGNL allows us to create quite more powerful expressions, and that's how this: ```html

Welcome to our grocery store, Sebastian Pepper!

``` ...obtains the user name by executing: ```java ((User) ctx.getVariable("session").get("user")).getName(); ``` But getter method navigation is just one of OGNL's features. Let's see some more: ```java /* * Access to properties using the point (.). Equivalent to calling property getters. */ ${person.father.name} /* * Access to properties can also be made by using brackets ([]) and writing * the name of the property as a variable or between single quotes. */ ${person['father']['name']} /* * If the object is a map, both dot and bracket syntax will be equivalent to * executing a call on its get(...) method. */ ${countriesByCode.ES} ${personsByName['Stephen Zucchini'].age} /* * Indexed access to arrays or collections is also performed with brackets, * writing the index without quotes. */ ${personsArray[0].name} /* * Methods can be called, even with arguments. */ ${person.createCompleteName()} ${person.createCompleteNameWithSeparator('-')} ``` ### Expression Basic Objects When evaluating OGNL expressions on the context variables, some objects are made available to expressions for higher flexibility. These objects will be referenced (per OGNL standard) starting with the `#` symbol: * `#ctx`: the context object. * `#vars:` the context variables. * `#locale`: the context locale. So we can do this: ```html Established locale country: US. ``` You can read the full reference of these objects in [Appendix A](#appendix-a-expression-basic-objects). ### Expression Utility Objects Besides these basic objects, Thymeleaf will offer us a set of utility objects that will help us perform common tasks in our expressions. * `#execInfo`: information about the template being processed. * `#messages`: methods for obtaining externalized messages inside variables expressions, in the same way as they would be obtained using #{...} syntax. * `#uris`: methods for escaping parts of URLs/URIs * `#conversions`: methods for executing the configured *conversion service* (if any). * `#dates`: methods for `java.util.Date` objects: formatting, component extraction, etc. * `#calendars`: analogous to `#dates`, but for `java.util.Calendar` objects. * `#temporals`: for dealing with dates and times using the `java.time` API in JDK8+. * `#numbers`: methods for formatting numeric objects. * `#strings`: methods for `String` objects: contains, startsWith, prepending/appending, etc. * `#objects`: methods for objects in general. * `#bools`: methods for boolean evaluation. * `#arrays`: methods for arrays. * `#lists`: methods for lists. * `#sets`: methods for sets. * `#maps`: methods for maps. * `#aggregates`: methods for creating aggregates on arrays or collections. * `#ids`: methods for dealing with id attributes that might be repeated (for example, as a result of an iteration). You can check what functions are offered by each of these utility objects in the [Appendix B](#appendix-b-expression-utility-objects). ### Reformatting dates in our home page Now we know about these utility objects, we could use them to change the way in which we show the date in our home page. Instead of doing this in our `HomeController`: ```java SimpleDateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy"); Calendar cal = Calendar.getInstance(); WebContext ctx = new WebContext(webExchange, webExchange.getLocale()); ctx.setVariable("today", dateFormat.format(cal.getTime())); templateEngine.process("home", ctx, writer); ``` ...we can do just this: ```java WebContext ctx = new WebContext(webExchange, webExchange.getLocale()); ctx.setVariable("today", Calendar.getInstance()); templateEngine.process("home", ctx, writer); ``` ...and then perform date formatting in the view layer itself: ```html

Today is: 13 May 2011

``` 4.3 Expressions on selections (asterisk syntax) ----------------------------------------------- Not only can variable expressions be written as `${...}`, but also as `*{...}`. There is an important difference though: the asterisk syntax evaluates expressions on _selected objects_ rather than on the whole context. That is, as long as there is no selected object, the dollar and the asterisk syntaxes do exactly the same. And what is a selected object? The result of an expression using the `th:object` attribute. Let's use one in our user profile (`userprofile.html`) page: ```html

Name: Sebastian.

Surname: Pepper.

Nationality: Saturn.

``` Which is exactly equivalent to: ```html

Name: Sebastian.

Surname: Pepper.

Nationality: Saturn.

``` Of course, dollar and asterisk syntax can be mixed: ```html

Name: Sebastian.

Surname: Pepper.

Nationality: Saturn.

``` When an object selection is in place, the selected object will also be available to dollar expressions as the `#object` expression variable: ```html

Name: Sebastian.

Surname: Pepper.

Nationality: Saturn.

``` As said, if no object selection has been performed, dollar and asterisk syntaxes are equivalent. ```html

Name: Sebastian.

Surname: Pepper.

Nationality: Saturn.

``` 4.4 Link URLs ------------- Because of their importance, URLs are first-class citizens in web application templates, and the _Thymeleaf Standard Dialect_ has a special syntax for them, the `@` syntax: `@{...}` There are different types of URLs: * Absolute URLs: `http://www.thymeleaf.org` * Relative URLs, which can be: * Page-relative: `user/login.html` * Context-relative: `/itemdetails?id=3` (context name in server will be added automatically) * Server-relative: `~/billing/processInvoice` (allows calling URLs in another context (= application) in the same server. * Protocol-relative URLs: `//code.jquery.com/jquery-2.0.3.min.js` The real processing of these expressions and their conversion to the URLs that will be output is done by implementations of the `org.thymeleaf.linkbuilder.ILinkBuilder` interface that are registered into the `ITemplateEngine` object being used. By default, a single implementation of this interface is registered of the class `org.thymeleaf.linkbuilder.StandardLinkBuilder`, which is enough for both offline (non-web) and also web scenarios based on the Servlet API. Other scenarios (like integration with non-ServletAPI web frameworks) might need specific implementations of the link builder interface. Let's use this new syntax. Meet the `th:href` attribute: ```html view view view ``` Some things to note here: * `th:href` is a modifier attribute: once processed, it will compute the link URL to be used and set that value to the `href` attribute of the `` tag. * We are allowed to use expressions for URL parameters (as you can see in `orderId=${o.id}`). The required URL-parameter-encoding operations will also be automatically performed. * If several parameters are needed, these will be separated by commas: `@{/order/process(execId=${execId},execType='FAST')}` * Variable templates are also allowed in URL paths: `@{/order/{orderId}/details(orderId=${orderId})}` * Relative URLs starting with `/` (eg: `/order/details`) will be automatically prefixed by the application context name. * If cookies are not enabled or this is not yet known, a `";jsessionid=..."` suffix might be added to relative URLs so that the session is preserved. This is called _URL Rewriting_ and Thymeleaf allows you to plug in your own rewriting filters by using the `response.encodeURL(...)` mechanism from the Servlet API for every URL. * The `th:href` attribute allows us to (optionally) have a working static `href` attribute in our template, so that our template links remained navigable by a browser when opened directly for prototyping purposes. As was the case with the message syntax (`#{...}`), URL bases can also be the result of evaluating another expression: ```html view view ``` ### A menu for our home page Now that we know how to create link URLs, what about adding a small menu in our home page for some of the other pages in the site? ```html

Please select an option

  1. Product List
  2. Order List
  3. Subscribe to our Newsletter
  4. See User Profile
``` ### Server root relative URLs An additional syntax can be used to create server-root-relative (instead of context-root-relative) URLs in order to link to different contexts in the same server. These URLs will be specified like `@{~/path/to/something}` 4.5 Fragments ------------- Fragment expressions are an easy way to represent fragments of markup and move them around templates. This allows us to replicate them, pass them to other templates as arguments, and so on. The most common use is for fragment insertion using `th:insert` or `th:replace` (more on these in a later section): ```html
...
``` But they can be used anywhere, just as any other variable: ```html

``` Later in this tutorial there is an entire section devoted to Template Layout, including deeper explanation of fragment expressions. 4.6 Literals ------------ ### Text literals Text literals are just character strings specified between single quotes. They can include any character, but you should escape any single quotes inside them using `\'`. ```html

Now you are looking at a template file.

``` ### Number literals Numeric literals are just that: numbers. ```html

The year is 1492.

In two years, it will be 1494.

``` ### Boolean literals The boolean literals are `true` and `false`. For example: ```html
... ``` In this example, the `== false` is written outside the braces, and so it is Thymeleaf that takes care of it. If it were written inside the braces, it would be the responsibility of the OGNL/SpringEL engines: ```html
... ``` ### The null literal The `null` literal can be also used: ```html
... ``` ### Literal tokens Numeric, boolean and null literals are in fact a particular case of _literal tokens_. These tokens allow a little bit of simplification in Standard Expressions. They work exactly the same as text literals (`'...'`), but they only allow letters (`A-Z` and `a-z`), numbers (`0-9`), brackets (`[` and `]`), dots (`.`), hyphens (`-`) and underscores (`_`). So no whitespaces, no commas, etc. The nice part? Tokens don't need any quotes surrounding them. So we can do this: ```html
...
``` instead of: ```html
...
``` 4.7 Appending texts ------------------- Texts, no matter whether they are literals or the result of evaluating variable or message expressions, can be easily appended using the `+` operator: ```html ``` 4.8 Literal substitutions ------------------------- Literal substitutions allow for an easy formatting of strings containing values from variables without the need to append literals with `'...' + '...'`. These substitutions must be surrounded by vertical bars (`|`), like: ```html ``` Which is equivalent to: ```html ``` Literal substitutions can be combined with other types of expressions: ```html ``` > Only variable/message expressions (`${...}`, `*{...}`, `#{...}`) are allowed > inside `|...|` literal substitutions. No other literals (`'...'`), > boolean/numeric tokens, conditional expressions etc. are. 4.9 Arithmetic operations ------------------------- Some arithmetic operations are also available: `+`, `-`, `*`, `/` and `%`. ```html
``` Note that these operators can also be applied inside OGNL variable expressions themselves (and in that case will be executed by OGNL instead of the Thymeleaf Standard Expression engine): ```html
``` Note that textual aliases exist for some of these operators: `div` (`/`), `mod` (`%`). 4.10 Comparators and Equality ----------------------------- Values in expressions can be compared with the `>`, `<`, `>=` and `<=` symbols, and the `==` and `!=` operators can be used to check for equality (or the lack of it). Note that XML establishes that the `<` and `>` symbols should not be used in attribute values, and so they should be substituted by `<` and `>`. ```html
``` A simpler alternative may be using textual aliases that exist for some of these operators: `gt` (`>`), `lt` (`<`), `ge` (`>=`), `le` (`<=`), `not` (`!`). Also `eq` (`==`), `neq`/`ne` (`!=`). 4.11 Conditional expressions ---------------------------- _Conditional expressions_ are meant to evaluate only one of two expressions depending on the result of evaluating a condition (which is itself another expression). Let's have a look at an example fragment (introducing another _attribute modifier_, `th:class`): ```html ... ``` All three parts of a conditional expression (`condition`, `then` and `else`) are themselves expressions, which means that they can be variables (`${...}`, `*{...}`), messages (`#{...}`), URLs (`@{...}`) or literals (`'...'`). Conditional expressions can also be nested using parentheses: ```html ... ``` Else expressions can also be omitted, in which case a null value is returned if the condition is false: ```html ... ``` 4.12 Default expressions (Elvis operator) ----------------------------------------- A _default expression_ is a special kind of conditional value without a _then_ part. It is equivalent to the _Elvis operator_ present in some languages like Groovy, lets you specify two expressions: the first one is used if it doesn't evaluate to null, but if it does then the second one is used. Let's see it in action in our user profile page: ```html
...

Age: 27.

``` As you can see, the operator is `?:`, and we use it here to specify a default value for a name (a literal value, in this case) only if the result of evaluating `*{age}` is null. This is therefore equivalent to: ```html

Age: 27.

``` As with conditional values, they can contain nested expressions between parentheses: ```html

Name: Sebastian

``` 4.13 The No-Operation token --------------------------- The No-Operation token is represented by an underscore symbol (`_`). The idea behind this token is to specify that the desired result for an expression is to *do nothing*, i.e. do exactly as if the processable attribute (e.g. `th:text`) was not there at all. Among other possibilities, this allows developers to use prototyping text as default values. For example, instead of: ```html ... ``` ...we can directly use *'no user authenticated'* as a prototyping text, which results in code that is both more concise and versatile from a design standpoint: ```html no user authenticated ``` 4.14 Data Conversion / Formatting --------------------------------- Thymeleaf defines a *double-brace* syntax for variable (`${...}`) and selection (`*{...}`) expressions that allows us to apply *data conversion* by means of a configured *conversion service*. It basically goes like this: ```html ... ``` Noticed the double brace there?: `${{...}}`. That instructs Thymeleaf to pass the result of the `user.lastAccessDate` expression to the *conversion service* and asks it to perform a **formatting operation** (a conversion to `String`) before writing the result. Assuming that `user.lastAccessDate` is of type `java.util.Calendar`, if a *conversion service* (implementation of `IStandardConversionService`) has been registered and contains a valid conversion for `Calendar -> String`, it will be applied. The default implementation of `IStandardConversionService` (the `StandardConversionService` class) simply executes `.toString()` on any object converted to `String`. For more information on how to register a custom *conversion service* implementation, have a look at the [More on Configuration](#more-on-configuration) section. > The official thymeleaf-spring3 and thymeleaf-spring4 integration packages > transparently integrate Thymeleaf's conversion service mechanism with Spring's > own *Conversion Service* infrastructure, so that conversion services and > formatters declared in the Spring configuration will be made automatically > available to `${{...}}` and `*{{...}}` expressions. 4.15 Preprocessing ------------------ In addition to all these features for expression processing, Thymeleaf has the feature of _preprocessing_ expressions. Preprocessing is an execution of the expressions done before the normal one that allows for modification of the expression that will eventually be executed. Preprocessed expressions are exactly like normal ones, but appear surrounded by a double underscore symbol (like `__${expression}__`). Let's imagine we have an i18n `Messages_fr.properties` entry containing an OGNL expression calling a language-specific static method, like: ```java article.text=@myapp.translator.Translator@translateToFrench({0}) ``` ...and a `Messages_es.properties equivalent`: ```java article.text=@myapp.translator.Translator@translateToSpanish({0}) ``` We can create a fragment of markup that evaluates one expression or the other depending on the locale. For this, we will first select the expression (by preprocessing) and then let Thymeleaf execute it: ```html

Some text here...

``` Note that the preprocessing step for a French locale will be creating the following equivalent: ```html

Some text here...

``` The preprocessing String `__` can be escaped in attributes using `\_\_`. 5 Setting Attribute Values ========================== This chapter will explain the way in which we can set (or modify) values of attributes in our markup. 5.1 Setting the value of any attribute -------------------------------------- Say our website publishes a newsletter, and we want our users to be able to subscribe to it, so we create a `/WEB-INF/templates/subscribe.html` template with a form: ```html
``` As with Thymeleaf, this template starts off more like a static prototype than it does a template for a web application. First, the `action` attribute in our form statically links to the template file itself, so that there is no place for useful URL rewriting. Second, the `value` attribute in the submit button makes it display a text in English, but we'd like it to be internationalized. Enter then the `th:attr` attribute, and its ability to change the value of attributes of the tags it is set in: ```html
``` The concept is quite straightforward: `th:attr` simply takes an expression that assigns a value to an attribute. Having created the corresponding controller and messages files, the result of processing this file will be: ```html
``` Besides the new attribute values, you can also see that the application context name has been automatically prefixed to the URL base in `/gtvg/subscribe`, as explained in the previous chapter. But what if we wanted to set more than one attribute at a time? XML rules do not allow you to set an attribute twice in a tag, so `th:attr` will take a comma-separated list of assignments, like: ```html ``` Given the required messages files, this will output: ```html Logo de Good Thymes ``` 5.2 Setting value to specific attributes ---------------------------------------- By now, you might be thinking that something like: ```html ``` ...is quite an ugly piece of markup. Specifying an assignment inside an attribute's value can be very practical, but it is not the most elegant way of creating templates if you have to do it all the time. Thymeleaf agrees with you, and that's why `th:attr` is scarcely used in templates. Normally, you will be using other `th:*` attributes whose task is setting specific tag attributes (and not just any attribute like `th:attr`). For example, to set the `value` attribute, use `th:value`: ```html ``` This looks much better! Let's try and do the same to the `action` attribute in the `form` tag: ```html
``` And do you remember those `th:href` we put in our `home.html` before? They are exactly this same kind of attributes: ```html
  • Product List
  • ``` There are quite a lot of attributes like these, each of them targeting a specific HTML5 attribute:
    ---------------------- ---------------------- ---------------------- `th:abbr` `th:accept` `th:accept-charset` `th:accesskey` `th:action` `th:align` `th:alt` `th:archive` `th:audio` `th:autocomplete` `th:axis` `th:background` `th:bgcolor` `th:border` `th:cellpadding` `th:cellspacing` `th:challenge` `th:charset` `th:cite` `th:class` `th:classid` `th:codebase` `th:codetype` `th:cols` `th:colspan` `th:compact` `th:content` `th:contenteditable` `th:contextmenu` `th:data` `th:datetime` `th:dir` `th:draggable` `th:dropzone` `th:enctype` `th:for` `th:form` `th:formaction` `th:formenctype` `th:formmethod` `th:formtarget` `th:fragment` `th:frame` `th:frameborder` `th:headers` `th:height` `th:high` `th:href` `th:hreflang` `th:hspace` `th:http-equiv` `th:icon` `th:id` `th:inline` `th:keytype` `th:kind` `th:label` `th:lang` `th:list` `th:longdesc` `th:low` `th:manifest` `th:marginheight` `th:marginwidth` `th:max` `th:maxlength` `th:media` `th:method` `th:min` `th:name` `th:onabort` `th:onafterprint` `th:onbeforeprint` `th:onbeforeunload` `th:onblur` `th:oncanplay` `th:oncanplaythrough` `th:onchange` `th:onclick` `th:oncontextmenu` `th:ondblclick` `th:ondrag` `th:ondragend` `th:ondragenter` `th:ondragleave` `th:ondragover` `th:ondragstart` `th:ondrop` `th:ondurationchange` `th:onemptied` `th:onended` `th:onerror` `th:onfocus` `th:onformchange` `th:onforminput` `th:onhashchange` `th:oninput` `th:oninvalid` `th:onkeydown` `th:onkeypress` `th:onkeyup` `th:onload` `th:onloadeddata` `th:onloadedmetadata` `th:onloadstart` `th:onmessage` `th:onmousedown` `th:onmousemove` `th:onmouseout` `th:onmouseover` `th:onmouseup` `th:onmousewheel` `th:onoffline` `th:ononline` `th:onpause` `th:onplay` `th:onplaying` `th:onpopstate` `th:onprogress` `th:onratechange` `th:onreadystatechange``th:onredo` `th:onreset` `th:onresize` `th:onscroll` `th:onseeked` `th:onseeking` `th:onselect` `th:onshow` `th:onstalled` `th:onstorage` `th:onsubmit` `th:onsuspend` `th:ontimeupdate` `th:onundo` `th:onunload` `th:onvolumechange` `th:onwaiting` `th:optimum` `th:pattern` `th:placeholder` `th:poster` `th:preload` `th:radiogroup` `th:rel` `th:rev` `th:rows` `th:rowspan` `th:rules` `th:sandbox` `th:scheme` `th:scope` `th:scrolling` `th:size` `th:sizes` `th:span` `th:spellcheck` `th:src` `th:srclang` `th:standby` `th:start` `th:step` `th:style` `th:summary` `th:tabindex` `th:target` `th:title` `th:type` `th:usemap` `th:value` `th:valuetype` `th:vspace` `th:width` `th:wrap` `th:xmlbase` `th:xmllang` `th:xmlspace` ---------------------- ---------------------- ----------------------
    5.3 Setting more than one value at a time ----------------------------------------- There are two rather special attributes called `th:alt-title` and `th:lang-xmllang` which can be used for setting two attributes to the same value at the same time. Specifically: * `th:alt-title` will set `alt` and `title`. * `th:lang-xmllang` will set `lang` and `xml:lang`. For our GTVG home page, this will allow us to substitute this: ```html ``` ...or this, which is equivalent: ```html ``` ...with this: ```html ``` 5.4 Appending and prepending ---------------------------- Thymeleaf also offers the `th:attrappend` and `th:attrprepend` attributes, which append (suffix) or prepend (prefix) the result of their evaluation to the existing attribute values. For example, you might want to store the name of a CSS class to be added (not set, just added) to one of your buttons in a context variable, because the specific CSS class to be used would depend on something that the user did before: ```html ``` If you process this template with the `cssStyle` variable set to `"warning"`, you will get: ```html ``` There are also two specific _appending attributes_ in the Standard Dialect: the `th:classappend` and `th:styleappend` attributes, which are used for adding a CSS class or a fragment of _style_ to an element without overwriting the existing ones: ```html ``` (Don't worry about that `th:each` attribute. It is an _iterating attribute_ and we will talk about it later.) 5.5 Fixed-value boolean attributes ---------------------------------- HTML has the concept of _boolean attributes_, attributes that have no value and the presence of one means that value is "true". In XHTML, these attributes take just 1 value, which is itself. For example, `checked`: ```html ``` The Standard Dialect includes attributes that allow you to set these attributes by evaluating a condition, so that if evaluated to true, the attribute will be set to its fixed value, and if evaluated to false, the attribute will not be set: ```html ``` The following fixed-value boolean attributes exist in the Standard Dialect:
    ------------------- ------------------ ------------------ `th:async` `th:autofocus` `th:autoplay` `th:checked` `th:controls` `th:declare` `th:default` `th:defer` `th:disabled` `th:formnovalidate` `th:hidden` `th:ismap` `th:loop` `th:multiple` `th:novalidate` `th:nowrap` `th:open` `th:pubdate` `th:readonly` `th:required` `th:reversed` `th:scoped` `th:seamless` `th:selected` ------------------- ------------------ ------------------
    5.6 Setting the value of any attribute (default attribute processor) -------------------------------------------------------------------- Thymeleaf offers a *default attribute processor* that allows us to set the value of *any* attribute, even if no specific `th:*` processor has been defined for it at the Standard Dialect. So something like: ```html ... ``` Will result in: ```html ... ``` 5.7 Support for HTML5-friendly attribute and element names ---------------------------------------------------------- It is also possible to use a completely different syntax to apply processors to your templates in a more HTML5-friendly manner. ```html
    ... ...
    ``` The `data-{prefix}-{name}` syntax is the standard way to write custom attributes in HTML5, without requiring developers to use any namespaced names like `th:*`. Thymeleaf makes this syntax automatically available to all your dialects (not only the Standard ones). There is also a syntax to specify custom tags: `{prefix}-{name}`, which follows the _W3C Custom Elements specification_ (a part of the larger _W3C Web Components spec_). This can be used, for example, for the `th:block` element (or also `th-block`), which will be explained in a later section. **Important:** this syntax is an addition to the namespaced `th:*` one, it does not replace it. There is no intention at all to deprecate the namespaced syntax in the future. 6 Iteration =========== So far we have created a home page, a user profile page and also a page for letting users subscribe to our newsletter... but what about our products? For that, we will need a way to iterate over items in a collection to build out our product page. 6.1 Iteration basics -------------------- To display products in our `/WEB-INF/templates/product/list.html` page we will use a table. Each of our products will be displayed in a row (a `` element), and so for our template we will need to create a _template row_ -- one that will exemplify how we want each product to be displayed -- and then instruct Thymeleaf to repeat it, once for each product. The Standard Dialect offers us an attribute for exactly that: `th:each`. ### Using th:each For our product list page, we will need a controller method that retrieves the list of products from the service layer and adds it to the template context: ```java public void process( final IWebExchange webExchange, final ITemplateEngine templateEngine, final Writer writer) throws Exception { final ProductService productService = new ProductService(); final List allProducts = productService.findAll(); final WebContext ctx = new WebContext(webExchange, webExchange.getLocale()); ctx.setVariable("prods", allProducts); templateEngine.process("product/list", ctx, writer); } ``` And then we will use `th:each` in our template to iterate over the list of products: ```html Good Thymes Virtual Grocery

    Product list

    NAME PRICE IN STOCK
    Onions 2.41 yes

    Return to home

    ``` That `prod : ${prods}` attribute value you see above means "for each element in the result of evaluating `${prods}`, repeat this fragment of template, using the current element in a variable called prod". Let's give a name each of the things we see: * We will call `${prods}` the _iterated expression_ or _iterated variable_. * We will call `prod` the _iteration variable_ or simply _iter variable_. Note that the `prod` iter variable is scoped to the `` element, which means it is available to inner tags like ``. ### Iterable values The `java.util.List` class isn't the only value that can be used for iteration in Thymeleaf. There is a quite complete set of objects that are considered _iterable_ by a `th:each` attribute: * Any object implementing `java.util.Iterable` * Any object implementing `java.util.Enumeration`. * Any object implementing `java.util.Iterator`, whose values will be used as they are returned by the iterator, without the need to cache all values in memory. * Any object implementing `java.util.Map`. When iterating maps, iter variables will be of class `java.util.Map.Entry`. * Any object implementing `java.util.stream.Stream`. * Any array. * Any other object will be treated as if it were a single-valued list containing the object itself. 6.2 Keeping iteration status ---------------------------- When using `th:each`, Thymeleaf offers a mechanism useful for keeping track of the status of your iteration: the _status variable_. Status variables are defined within a `th:each` attribute and contain the following data: * The current _iteration index_, starting with 0. This is the `index` property. * The current _iteration index_, starting with 1. This is the `count` property. * The total amount of elements in the iterated variable. This is the `size` property. * The _iter variable_ for each iteration. This is the `current` property. * Whether the current iteration is even or odd. These are the `even/odd` boolean properties. * Whether the current iteration is the first one. This is the `first` boolean property. * Whether the current iteration is the last one. This is the `last` boolean property. Let's see how we could use it with the previous example: ```html
    NAME PRICE IN STOCK
    Onions 2.41 yes
    ``` The status variable (`iterStat` in this example) is defined in the `th:each` attribute by writing its name after the iter variable itself, separated by a comma. Just like the iter variable, the status variable is also scoped to the fragment of code defined by the tag holding the `th:each` attribute. Let's have a look at the result of processing our template: ```html Good Thymes Virtual Grocery

    Product list

    NAME PRICE IN STOCK
    Fresh Sweet Basil 4.99 yes
    Italian Tomato 1.25 no
    Yellow Bell Pepper 2.50 yes
    Old Cheddar 18.75 yes

    Return to home

    ``` Note that our iteration status variable has worked perfectly, establishing the `odd` CSS class only to odd rows. If you don't explicitly set a status variable, Thymeleaf will always create one for you by suffixing `Stat` to the name of the iteration variable: ```html
    NAME PRICE IN STOCK
    Onions 2.41 yes
    ``` 6.3 Optimizing through lazy retrieval of data --------------------------------------------- Sometimes we might want to optimize the retrieval of collections of data (e.g. from a database) so that these collections are only retrieved if they are really going to be used. > Actually, this is something that can be applied to *any* piece of data, but given the size > that in-memory collections might have, retrieving collections that are meant to be iterated > is the most common case for this scenario. In order to support this, Thymeleaf offers a mechanism to *lazily load context variables*. Context variables that implement the `ILazyContextVariable` interface -- most probably by extending its `LazyContextVariable` default implementation -- will be resolved in the moment of being executed. For example: ```java context.setVariable( "users", new LazyContextVariable>() { @Override protected List loadValue() { return databaseRepository.findAllUsers(); } }); ``` This variable can be used without knowledge of its *laziness*, in code such as: ```html
    • user name
    ``` But at the same time, will never be initialized (its `loadValue()` method will never be called) if `condition` evaluates to `false` in code such as: ```html
    • user name
    ``` 7 Conditional Evaluation ======================== 7.1 Simple conditionals: "if" and "unless" ------------------------------------------ Sometimes you will need a fragment of your template to only appear in the result if a certain condition is met. For example, imagine we want to show in our product table a column with the number of comments that exist for each product and, if there are any comments, a link to the comment detail page for that product. In order to do this, we would use the `th:if` attribute: ```html
    NAME PRICE IN STOCK COMMENTS
    Onions 2.41 yes 2 comment/s view
    ``` Quite a lot of things to see here, so let's focus on the important line: ```html view ``` This will create a link to the comments page (with URL `/product/comments`) with a `prodId` parameter set to the `id` of the product, but only if the product has any comments. Let's have a look at the resulting markup: ```html
    NAME PRICE IN STOCK COMMENTS
    Fresh Sweet Basil 4.99 yes 0 comment/s
    Italian Tomato 1.25 no 2 comment/s view
    Yellow Bell Pepper 2.50 yes 0 comment/s
    Old Cheddar 18.75 yes 1 comment/s view
    ``` Perfect! That's exactly what we wanted. Note that the `th:if` attribute will not only evaluate _boolean_ conditions. Its capabilities go a little beyond that, and it will evaluate the specified expression as `true` following these rules: * If value is not null: * If value is a boolean and is `true`. * If value is a number and is non-zero * If value is a character and is non-zero * If value is a String and is not "false", "off" or "no" * If value is not a boolean, a number, a character or a String. * (If value is null, th:if will evaluate to false). Also, `th:if` has an inverse attribute, `th:unless`, which we could have used in the previous example instead of using a `not` inside the OGNL expression: ```html view ``` 7.2 Switch statements --------------------- There is also a way to display content conditionally using the equivalent of a _switch_ structure in Java: the `th:switch` / `th:case` attribute set. ```html

    User is an administrator

    User is a manager

    ``` Note that as soon as one `th:case` attribute is evaluated as `true`, every other `th:case` attribute in the same switch context is evaluated as `false`. The default option is specified as `th:case="*"`: ```html

    User is an administrator

    User is a manager

    User is some other thing

    ``` 8 Template Layout ================= 8.1 Including template fragments -------------------------------- ### Defining and referencing fragments In our templates, we will often want to include parts from other templates, parts like footers, headers, menus... In order to do this, Thymeleaf needs us to define these parts, "fragments", for inclusion, which can be done using the `th:fragment` attribute. Say we want to add a standard copyright footer to all our grocery pages, so we create a `/WEB-INF/templates/footer.html` file containing this code: ```html
    © 2011 The Good Thymes Virtual Grocery
    ``` The code above defines a fragment called `copy` that we can easily include in our home page using one of the `th:insert` or `th:replace` attributes: ```html ...
    ``` Note that `th:insert` expects a *fragment expression* (`~{...}`), which is *an expression that results in a fragment*. ### Fragment specification syntax The syntax of *fragment expressions* is quite straightforward. There are three different formats: * `"~{templatename::selector}"` Includes the fragment resulting from applying the specified Markup Selector on the template named `templatename`. Note that `selector` can be a mere fragment name, so you could specify something as simple as `~{templatename::fragmentname}` like in the `~{footer :: copy}` above. > Markup Selector syntax is defined by the underlying AttoParser parsing > library, and is similar to XPath expressions or CSS selectors. See > [Appendix C](#appendix-c-markup-selector-syntax) for more info. * `"~{templatename}"` Includes the complete template named `templatename`. > Note that the template name you use in `th:insert`/`th:replace` tags > will have to be resolvable by the Template Resolver currently being used by > the Template Engine. * `~{::selector}"` or `"~{this::selector}"` Inserts a fragment from the same template, matching `selector`. If not found on the template where the expression appears, the stack of template calls (insertions) is traversed towards the originally processed template (the *root*), until `selector` matches at some level. Both `templatename` and `selector` in the above examples can be fully-featured expressions (even conditionals!) like: ```html
    ``` Fragments can include any `th:*` attributes. These attributes will be evaluated once the fragment is included into the target template (the one with the `th:insert`/`th:replace` attribute), and they will be able to reference any context variables defined in this target template. > A big advantage of this approach to fragments is that you can write your > fragments in pages that are perfectly displayable by a browser, with a > complete and even *valid* markup structure, while still retaining the ability > to make Thymeleaf include them into other templates. ### Referencing fragments without `th:fragment` Thanks to the power of Markup Selectors, we can include fragments that do not use any `th:fragment` attributes. It can even be markup code coming from a different application with no knowledge of Thymeleaf at all: ```html ...
    © 2011 The Good Thymes Virtual Grocery
    ... ``` We can use the fragment above simply referencing it by its `id` attribute, in a similar way to a CSS selector: ```html ...
    ``` ### Difference between `th:insert` and `th:replace` And what is the difference between `th:insert` and `th:replace`? * `th:insert` will simply insert the specified fragment as the body of its host tag. * `th:replace` actually *replaces* its host tag with the specified fragment. So an HTML fragment like this: ```html
    © 2011 The Good Thymes Virtual Grocery
    ``` ...included twice in host `
    ` tags, like this: ```html ...
    ``` ...will result in: ```html ...
    © 2011 The Good Thymes Virtual Grocery
    © 2011 The Good Thymes Virtual Grocery
    ``` 8.2 Parameterizable fragment signatures --------------------------------------- In order to create a more _function-like_ mechanism for template fragments, fragments defined with `th:fragment` can specify a set of parameters: ```html

    ...

    ``` This requires the use of one of these two syntaxes to call the fragment from `th:insert` or `th:replace`: ```html
    ...
    ...
    ``` Note that order is not important in the last option: ```html
    ...
    ``` ### Fragment local variables without fragment arguments Even if fragments are defined without arguments like this: ```html
    ...
    ``` We could use the second syntax specified above to call them (and only the second one): ```html
    ``` This would be equivalent to a combination of `th:replace` and `th:with`: ```html
    ``` **Note** that this specification of local variables for a fragment -- no matter whether it has an argument signature or not -- does not cause the context to be emptied prior to its execution. Fragments will still be able to access every context variable being used at the calling template like they currently are. ### th:assert for in-template assertions The `th:assert` attribute can specify a comma-separated list of expressions which should be evaluated and produce true for every evaluation, raising an exception if not. ```html
    ...
    ``` This comes in handy for validating parameters at a fragment signature: ```html
    ...
    ``` 8.3 Flexible layouts: beyond mere fragment insertion ---------------------------------------------------- Thanks to *fragment expressions*, we can specify parameters for fragments that are not texts, numbers, bean objects... but instead fragments of markup. This allows us to create our fragments in a way such that they can be *enriched* with markup coming from the calling templates, resulting in a very flexible **template layout mechanism**. Note the use of the `title` and `links` variables in the fragment below: ```html The awesome application ``` We can now call this fragment like: ```html ... Awesome - Main ... ``` ...and the result will use the actual `` and `<link>` tags from our calling template as the values of the `title` and `links` variables, resulting in our fragment being customized during insertion: ```html ... <head> <title>Awesome - Main ... ``` ### Using the empty fragment A special fragment expression, the *empty fragment* (`~{}`), can be used for specifying *no markup*. Using the previous example: ```html Awesome - Main ... ``` Note how the second parameter of the fragment (`links`) is set to the *empty fragment* and therefore nothing is written for the `` block: ```html ... Awesome - Main ... ``` ### Using the no-operation token The no-op can be also used as a parameter to a fragment if we just want to let our fragment use its current markup as a default value. Again, using the `common_header` example: ```html ... Awesome - Main ... ``` See how the `title` argument (first argument of the `common_header` fragment) is set to *no-op* (`_`), which results in this part of the fragment not being executed at all (`title` = *no-operation*): ```html The awesome application ``` So the result is: ```html ... The awesome application ... ``` ### Advanced conditional insertion of fragments The availability of both the *empty fragment* and *no-operation token* allows us to perform conditional insertion of fragments in a very easy and elegant way. For example, we could do this in order to insert our `common :: adminhead` fragment *only* if the user is an administrator, and insert nothing (empty fragment) if not: ```html ...
    ...
    ... ``` Also, we can use the *no-operation token* in order to insert a fragment only if the specified condition is met, but leave the markup without modifications if the condition is not met: ```html ...
    Welcome [[${user.name}]], click here for help-desk support.
    ... ``` Additionally, if we have configured our template resolvers to *check for existence* of the template resources –- by means of their `checkExistence` flag -– we can use the existence of the fragment itself as the condition in a *default* operation: ```html ...
    Welcome [[${user.name}]], click here for help-desk support.
    ... ``` 8.4 Removing template fragments ------------------------------- Back to the example application, let's revisit the last version of our product list template: ```html
    NAME PRICE IN STOCK COMMENTS
    Onions 2.41 yes 2 comment/s view
    ``` This code is just fine as a template, but as a static page (when directly open by a browser without Thymeleaf processing it) it would not make a nice prototype. Why? Because, although perfectly displayable by browsers, that table only has a row, and this row has mock data. As a prototype, it simply wouldn't look realistic enough... we should have more than one product, _we need more rows_. So let's add some: ```html
    NAME PRICE IN STOCK COMMENTS
    Onions 2.41 yes 2 comment/s view
    Blue Lettuce 9.55 no 0 comment/s
    Mild Cinnamon 1.99 yes 3 comment/s view
    ``` Ok, now we have three, definitely better for a prototype. But... what will happen when we process it with Thymeleaf?: ```html
    NAME PRICE IN STOCK COMMENTS
    Fresh Sweet Basil 4.99 yes 0 comment/s
    Italian Tomato 1.25 no 2 comment/s view
    Yellow Bell Pepper 2.50 yes 0 comment/s
    Old Cheddar 18.75 yes 1 comment/s view
    Blue Lettuce 9.55 no 0 comment/s
    Mild Cinnamon 1.99 yes 3 comment/s view
    ``` The last two rows are mock rows! Well, of course they are: iteration was only applied to the first row, so there is no reason why Thymeleaf should have removed the other two. We need a way to remove those two rows during template processing. Let's use the `th:remove` attribute on the second and third `` tags: ```html
    NAME PRICE IN STOCK COMMENTS
    Onions 2.41 yes 2 comment/s view
    Blue Lettuce 9.55 no 0 comment/s
    Mild Cinnamon 1.99 yes 3 comment/s view
    ``` Once processed, everything will look again as it should: ```html
    NAME PRICE IN STOCK COMMENTS
    Fresh Sweet Basil 4.99 yes 0 comment/s
    Italian Tomato 1.25 no 2 comment/s view
    Yellow Bell Pepper 2.50 yes 0 comment/s
    Old Cheddar 18.75 yes 1 comment/s view
    ``` And what does that `all` value in the attribute, mean? `th:remove` can behave in five different ways, depending on its value: * `all`: Remove both the containing tag and all its children. * `body`: Do not remove the containing tag, but remove all its children. * `tag`: Remove the containing tag, but do not remove its children. * `all-but-first`: Remove all children of the containing tag except the first one. * `none` : Do nothing. This value is useful for dynamic evaluation. What can that `all-but-first` value be useful for? It will let us save some `th:remove="all"` when prototyping: ```html
    NAME PRICE IN STOCK COMMENTS
    Onions 2.41 yes 2 comment/s view
    Blue Lettuce 9.55 no 0 comment/s
    Mild Cinnamon 1.99 yes 3 comment/s view
    ``` The `th:remove` attribute can take any _Thymeleaf Standard Expression_, as long as it returns one of the allowed String values (`all`, `tag`, `body`, `all-but-first` or `none`). This means removals could be conditional, like: ```html Link text not to be removed ``` Also note that `th:remove` considers `null` a synonym to `none`, so the following works the same as the example above: ```html Link text not to be removed ``` In this case, if `${condition}` is false, `null` will be returned, and thus no removal will be performed. 8.5 Layout Inheritance ---------------------- To be able to have a single file as layout, fragments can be used. An example of a simple layout having `title` and `content` using `th:fragment` and `th:replace`: ```html Layout Title

    Layout H1

    Layout content

    Layout footer
    ``` This example declares a fragment called **layout** having _title_ and _content_ as parameters. Both will be replaced on page inheriting it by provided fragment expressions in the example below. ```html Page Title

    Page content

    Included on page
    ``` In this file, the `html` tag will be replaced by _layout_, but in the layout `title` and `content` will have been replaced by `title` and `section` blocks respectively. If desired, the layout can be composed by several fragments as _header_ and _footer_. 9 Local Variables ================= Thymeleaf calls _local variables_ the variables that are defined for a specific fragment of a template, and are only available for evaluation inside that fragment. An example we have already seen is the `prod` iter variable in our product list page: ```html ... ``` That `prod` variable will be available only within the bounds of the `` tag. Specifically: * It will be available for any other `th:*` attributes executing in that tag with less _precedence_ than `th:each` (which means they will execute after `th:each`). * It will be available for any child element of the `` tag, such as any `` elements. Thymeleaf offers you a way to declare local variables without iteration, using the `th:with` attribute, and its syntax is like that of attribute value assignments: ```html

    The name of the first person is Julius Caesar.

    ``` When `th:with` is processed, that `firstPer` variable is created as a local variable and added to the variables map coming from the context, so that it is available for evaluation along with any other variables declared in the context, but only within the bounds of the containing `
    ` tag. You can define several variables at the same time using the usual multiple assignment syntax: ```html

    The name of the first person is Julius Caesar.

    But the name of the second person is Marcus Antonius.

    ``` The `th:with` attribute allows reusing variables defined in the same attribute: ```html
    ...
    ``` Let's use this in our Grocery's home page! Remember the code we wrote for outputting a formatted date? ```html

    Today is: 13 february 2011

    ``` Well, what if we wanted that `"dd MMMM yyyy"` to actually depend on the locale? For example, we might want to add the following message to our `home_en.properties`: ``` date.format=MMMM dd'','' yyyy ``` ...and an equivalent one to our `home_es.properties`: ``` date.format=dd ''de'' MMMM'','' yyyy ``` Now, let's use `th:with` to get the localized date format into a variable, and then use it in our `th:text` expression: ```html

    Today is: 13 February 2011

    ``` That was clean and easy. In fact, given the fact that `th:with` has a higher `precedence` than `th:text`, we could have solved this all in the `span` tag: ```html

    Today is: 13 February 2011

    ``` You might be thinking: Precedence? We haven't talked about that yet! Well, don't worry because that is exactly what the next chapter is about. 10 Attribute Precedence ======================= What happens when you write more than one `th:*` attribute in the same tag? For example: ```html
    • Item description here...
    ``` We would expect that `th:each` attribute to execute before the `th:text` so that we get the results we want, but given the fact that the HTML/XML standards do not give any kind of meaning to the order in which the attributes in a tag are written, a _precedence_ mechanism had to be established in the attributes themselves in order to be sure that this will work as expected. So, all Thymeleaf attributes define a numeric precedence, which establishes the order in which they are executed in the tag. This order is:
    ----------------------------------------------------------------- Order Feature Attributes ------- ---------------------------------- ---------------------- 1 Fragment inclusion `th:insert`\ `th:replace` 2 Fragment iteration `th:each` 3 Conditional evaluation `th:if`\ `th:unless`\ `th:switch`\ `th:case` 4 Local variable definition `th:object`\ `th:with` 5 General attribute modification `th:attr`\ `th:attrprepend`\ `th:attrappend` 6 Specific attribute modification `th:value`\ `th:href`\ `th:src`\ `...` 7 Text (tag body modification) `th:text`\ `th:utext` 8 Fragment specification `th:fragment` 9 Fragment removal `th:remove` -----------------------------------------------------------------
    This precedence mechanism means that the above iteration fragment will give exactly the same results if the attribute position is inverted (although it would be slightly less readable): ```html
    • Item description here...
    ``` 11 Comments and Blocks ====================== 11.1. Standard HTML/XML comments -------------------------------- Standard HTML/XML comments `` can be used anywhere in Thymeleaf templates. Anything inside these comments will be processed by Thymeleaf as any other block of text in the template: ```html
    ...
    ``` 11.2. Thymeleaf parser-level comment blocks ------------------------------------------- Parser-level comment blocks are code that will be simply removed from the template when Thymeleaf parses it. They look like this: ```html ``` Thymeleaf will remove everything between ``, so these comment blocks can also be used for displaying code when a template is statically open, knowing that it will be removed when Thymeleaf processes it: ```html
    you can see me only before Thymeleaf processes me!
    ``` This might come very handy for prototyping tables with a lot of ``'s, for example: ```html ... ... ...
    ``` 11.3. Thymeleaf prototype-only comment blocks --------------------------------------------- Thymeleaf allows the definition of special comment blocks marked to be comments when the template is open statically (i.e. as a prototype), but considered normal markup by Thymeleaf when executing the template. ```html hello! goodbye! ``` Thymeleaf's parsing system will simply remove the `` markers, but not its contents, which will be left therefore uncommented. So when executing the template, Thymeleaf will actually see this: ```html hello!
    ...
    goodbye! ``` As with parser-level comment blocks, this feature is dialect-independent. 11.4. Synthetic `th:block` tag ------------------------------ Thymeleaf's only element processor (not an attribute) included in the Standard Dialects is `th:block`. `th:block` is a mere attribute container that allows template developers to specify whichever attributes they want. Thymeleaf will execute these attributes and then simply make the block, but not its contents, disappear. So it could be useful, for example, when creating iterated tables that require more than one `` for each element: ```html
    ... ...
    ...
    ``` And especially useful when used in combination with prototype-only comment blocks: ```html
    ... ...
    ...
    ``` Note how this solution allows templates to be valid HTML (no need to add forbidden `
    ` blocks inside ``), and still works OK when open statically in browsers as prototypes! 12 Inlining =========== 12.1 Expression inlining ------------------------ Although the Standard Dialect allows us to do almost everything using tag attributes, there are situations in which we could prefer writing expressions directly into our HTML texts. For example, we could prefer writing this: ```html

    Hello, [[${session.user.name}]]!

    ``` ...instead of this: ```html

    Hello, Sebastian!

    ``` Expressions between `[[...]]` or `[(...)]` are considered **inlined expressions** in Thymeleaf, and inside them we can use any kind of expression that would also be valid in a `th:text` or `th:utext` attribute. Note that, while `[[...]]` corresponds to `th:text` (i.e. result will be *HTML-escaped*), `[(...)]` corresponds to `th:utext` and will not perform any HTML-escaping. So with a variable such as `msg = 'This is great!'`, given this fragment: ```html

    The message is "[(${msg})]"

    ``` The result will have those `` tags unescaped, so: ```html

    The message is "This is great!"

    ``` Whereas if escaped like: ```html

    The message is "[[${msg}]]"

    ``` The result will be HTML-escaped: ```html

    The message is "This is <b>great!</b>"

    ``` Note that **text inlining is active by default** in the body of every tag in our markup –- not the tags themselves -–, so there is nothing we need to do to enable it. ### Inlining vs natural templates If you come from other template engines in which this way of outputting text is the norm, you might be asking: _Why aren't we doing this from the beginning? It's less code than all those_ `th:text` _attributes!_ Well, be careful there, because although you might find inlining quite interesting, you should always remember that inlined expressions will be displayed verbatim in your HTML files when you open them statically, so you probably won't be able to use them as design prototypes anymore! The difference between how a browser would statically display our fragment of code without using inlining... ``` Hello, Sebastian! ``` ...and using it... ``` Hello, [[${session.user.name}]]! ``` ...is quite clear in terms of design usefulness. ### Disabling inlining This mechanism can be disabled though, because there might actually be occasions in which we do want to output the `[[...]]` or `[(...)]` sequences without its contents being processed as an expression. For that, we will use `th:inline="none"`: ```html

    A double array looks like this: [[1, 2, 3], [4, 5]]!

    ``` This will result in: ```html

    A double array looks like this: [[1, 2, 3], [4, 5]]!

    ``` 12.2 Text inlining ------------------ *Text inlining* is very similar to the *expression inlining* capability we have just seen, but it actually adds more power. It has to be enabled explicitly with `th:inline="text"`. Text inlining not only allows us to use the same *inlined expressions* we just saw, but in fact processes *tag bodies* as if they were templates processed in the `TEXT` template mode, which allows us to perform text-based template logic (not only output expressions). We will see more about this in the next chapter about the *textual template modes*. 12.3 JavaScript inlining ------------------------ JavaScript inlining allows for a better integration of JavaScript ` ``` This will result in: ```html ``` Two important things to note in the code above: *First*, that JavaScript inlining will not only output the required text, but also enclose it with quotes and JavaScript-escape its contents, so that the expression results are output as a **well-formed JavaScript literal**. *Second*, that this is happening because we are outputting the `${session.user.name}` expression as **escaped**, i.e. using a double-bracket expression: `[[${session.user.name}]]`. If instead we used *unescaped* like: ```html ``` The result would look like: ```html ``` ...which is malformed JavaScript code. But outputting something unescaped might be what we need if we are building parts of our script by means of appending inlined expressions, so it's good to have this tool at hand. ### JavaScript natural templates The mentioned *intelligence* of the JavaScript inlining mechanism goes much further than just applying JavaScript-specific escaping and outputting expression results as valid literals. For example, we can wrap our (escaped) inlined expressions in JavaScript comments like: ```html ``` And Thymeleaf will ignore everything we have written *after the comment and before the semicolon* (in this case ` 'Gertrud Kiwifruit'`), so the result of executing this will look exactly like when we were not using the wrapping comments: ```html ``` But have another careful look at the original template code: ```html ``` Note how this is **valid JavaScript** code. And it will perfectly execute when you open your template file in a static manner (without executing it at a server). So what we have here is a way to do **JavaScript natural templates**! ### Advanced inlined evaluation and JavaScript serialization An important thing to note regarding JavaScript inlining is that this expression evaluation is intelligent and not limited to Strings. Thymeleaf will correctly write in JavaScript syntax the following kinds of objects: * Strings * Numbers * Booleans * Arrays * Collections * Maps * Beans (objects with _getter_ and _setter_ methods) For example, if we had the following code: ```html ``` That `${session.user}` expression will evaluate to a `User` object, and Thymeleaf will correctly convert it to Javascript syntax: ```html ``` The way this JavaScript serialization is done is by means of an implementation of the `org.thymeleaf.standard.serializer.IStandardJavaScriptSerializer` interface, which can be configured at the instance of the `StandardDialect` being used at the template engine. The default implementation of this JS serialization mechanism will look for the [Jackson library](https://github.com/FasterXML/jackson) in the classpath and, if present, will use it. If not, it will apply a built-in serialization mechanism that covers the needs of most scenarios and produces similar results (but is less flexible). 12.4 CSS inlining ----------------- Thymeleaf also allows the use of inlining in CSS ` ``` For example, say we have two variables set to two different `String` values: ``` classname = 'main elems' align = 'center' ``` We could use them just like: ```html ``` And the result would be: ```html ``` Note how CSS inlining also bears some *intelligence*, just like JavaScript's. Specifically, expressions output via *escaped* expressions like `[[${classname}]]` will be escaped as **CSS identifiers**. That is why our `classname = 'main elems'` has turned into `main\ elems` in the fragment of code above. ### Advanced features: CSS natural templates, etc. In an equivalent way to what was explained before for JavaScript, CSS inlining also allows for our ` ``` 13 Textual template modes ========================= 13.1 Textual syntax ------------------- Three of the Thymeleaf *template modes* are considered **textual**: `TEXT`, `JAVASCRIPT` and `CSS`. This differentiates them from the markup template modes: `HTML` and `XML`. The key difference between *textual* template modes and the markup ones is that in a textual template there are no tags into which to insert logic in the form of attributes, so we have to rely on other mechanisms. The first and most basic of these mechanisms is **inlining**, which we have already detailed in the previous chapter. Inlining syntax is the most simple way to output results of expressions in textual template mode, so this is a perfectly valid template for a text email. ``` Dear [(${name})], Please find attached the results of the report you requested with name "[(${report.name})]". Sincerely, The Reporter. ``` Even without tags, the example above is a complete and valid Thymeleaf template that can be executed in the `TEXT` template mode. But in order to include more complex logic than mere *output expressions*, we need a new non-tag-based syntax: ``` [# th:each="item : ${items}"] - [(${item})] [/] ``` Which is actually the *condensed* version of the more verbose: ``` [#th:block th:each="item : ${items}"] - [#th:block th:utext="${item}" /] [/th:block] ``` Note how this new syntax is based on elements (i.e. processable tags) that are declared as `[#element ...]` instead of ``. Elements are open like `[#element ...]` and closed like `[/element]`, and standalone tags can be declared by minimizing the open element with a `/` in a way almost equivalent to XML tags: `[#element ... /]`. The Standard Dialect only contains a processor for one of these elements: the already-known `th:block`, though we could extend this in our dialects and create new elements in the usual way. Also, the `th:block` element (`[#th:block ...] ... [/th:block]`) is allowed to be abbreviated as the empty string (`[# ...] ... [/]`), so the above block is actually equivalent to: ``` [# th:each="item : ${items}"] - [# th:utext="${item}" /] [/] ``` And given `[# th:utext="${item}" /]` is equivalent to an *inlined unescaped expression*, we could just use it in order to have less code. Thus we end up with the first fragment of code we saw above: ``` [# th:each="item : ${items}"] - [(${item})] [/] ``` Note that the *textual syntax requires full element balance (no unclosed tags) and quoted attributes* -- it's more XML-style than HTML-style. Let's have a look at a more complete example of a `TEXT` template, a *plain text* email template: ``` Dear [(${customer.name})], This is the list of our products: [# th:each="prod : ${products}"] - [(${prod.name})]. Price: [(${prod.price})] EUR/kg [/] Thanks, The Thymeleaf Shop ``` After executing, the result of this could be something like: ``` Dear Mary Ann Blueberry, This is the list of our products: - Apricots. Price: 1.12 EUR/kg - Bananas. Price: 1.78 EUR/kg - Apples. Price: 0.85 EUR/kg - Watermelon. Price: 1.91 EUR/kg Thanks, The Thymeleaf Shop ``` And another example in `JAVASCRIPT` template mode, a `greeter.js` file, we process as a textual template and which result we call from our HTML pages. Note this is *not* a `