注册 登录
  • 欢迎访问开心洋葱网站,在线教程,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站,欢迎加入开心洋葱 QQ群
  • 为方便开心用户,开心洋葱官网已经开启复制功能!
  • 欢迎访问开心洋葱网站,手机也能访问哦~欢迎加入开心洋葱多维思维学习平台 QQ群
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏开心洋葱吧~~~~~~~~~~~~~!

Thymeleaf中文手册-语法速查手册-中文帮助手册

其他 开心洋葱 2801次浏览 已收录 0个评论 手机上查看
文章索引目录
[隐藏]

Thymeleaf中文手册-语法速查手册-中文帮助手册

1.Thymeleaf

2.Tutorial: Using Thymeleaf

3.1 Introducing Thymeleaf

3.1.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.

3.2.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.

3.3.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:
<form:inputText name="userName" value="${user.name}" />
…the Thymeleaf Standard Dialect would allow us to achieve the same functionality with:
<input type="text" name="userName" value="James Carrot" th:value="${user.name}" />
Not only will this be correctly displayed by browsers, but this also allow 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.

4.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 GitHub repository.

4.1.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:
Thymeleaf中文手册-语法速查手册-中文帮助手册
Example application model
Our application will also have a very simple service layer, composed by Service objects containing methods like:
public class ProductService {

    ...

    public List<Product> 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:
private boolean process(HttpServletRequest request, HttpServletResponse response)
        throws ServletException {

    try {

        // 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.
         */
        IGTVGController controller = this.application.resolveControllerForRequest(request);
        if (controller == null) {
            return false;
        }

        /*
         * Obtain the TemplateEngine instance.
         */
        ITemplateEngine templateEngine = this.application.getTemplateEngine();

        /*
         * 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);

        /*
         * Execute the controller and process view template,
         * writing the results to the response writer. 
         */
        controller.process(
                request, response, this.servletContext, templateEngine);

        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:
public interface IGTVGController {

    public void process(
            HttpServletRequest request, HttpServletResponse response,
            ServletContext servletContext, ITemplateEngine templateEngine);    

}
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:
Thymeleaf中文手册-语法速查手册-中文帮助手册
Example application home page
But first let’s see how that template engine is initialized.

4.2.2.2 Creating and configuring the Template Engine

The process(…) method in our filter contained this line:
ITemplateEngine templateEngine = this.application.getTemplateEngine();
Which means that the GTVGApplication class is in charge of creating and configuring one of the most important objects in a Thymeleaf application: the TemplateEngine instance (implementation of the ITemplateEngine interface). Our org.thymeleaf.TemplateEngine object is initialized like this:
public class GTVGApplication {

    ...
    private final TemplateEngine templateEngine;
    ...

    public GTVGApplication(final ServletContext servletContext) {

        super();

        ServletContextTemplateResolver templateResolver = 
                new ServletContextTemplateResolver(servletContext);

        // HTML is the default mode, but we 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");
        // Template cache TTL=1h. If not set, entries would be cached until expelled
        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);

        this.templateEngine = new TemplateEngine();
        this.templateEngine.setTemplateResolver(templateResolver);

        ...

    }

}
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.

4.2.1.The Template Resolver

Let’s start with the Template Resolver:
ServletContextTemplateResolver templateResolver = 
        new ServletContextTemplateResolver(servletContext);
Template Resolvers are objects that implement an interface from the Thymeleaf API called org.thymeleaf.templateresolver.ITemplateResolver:
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<String, Object> templateResolutionAttributes);
}
These objects are in charge of determining how our templates will be accessed, and in this GTVG application, the org.thymeleaf.templateresolver.ServletContextTemplateResolver means that we are going to retrieve our template files as resources from the Servlet Context: an application-wide javax.servlet.ServletContext object that exists in every Java web application, 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:
templateResolver.setTemplateMode(TemplateMode.HTML);
HTML is the default template mode for ServletContextTemplateResolver, but it is good practice to establish it anyway so that our code documents clearly what is going on.
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:
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:
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.

4.2.2.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:
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.

5.3 Using Texts

5.1.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 xmlns:th="http://www.thymeleaf.org">

  <head>
    <title>Good Thymes Virtual Grocerytitle>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <link rel="stylesheet" type="text/css" media="all" 
          href="../../css/gtvg.css" th:href="@{/css/gtvg.css}" />
  head>

  <body>

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

  body>

html>
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 xmlns:th="http://www.thymeleaf.org">
…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>

  <head>
    <title>Good Thymes Virtual Grocerytitle>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <link rel="stylesheet" type="text/css" media="all" 
          href="../../css/gtvg.css" data-th-href="@{/css/gtvg.css}" />
  head>

  <body>

    <p data-th-text="#{home.welcome}">Welcome to our grocery store!p>

  body>

html>
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.

5.1.1.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:
<p th:text="#{home.welcome}">Welcome to our grocery store!p>
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.

5.1.2.Contexts

In order to process our template, we will create a HomeController class implementing the IGTVGController interface we saw before:
public class HomeController implements IGTVGController {

    public void process(
            final HttpServletRequest request, final HttpServletResponse response,
            final ServletContext servletContext, final ITemplateEngine templateEngine)
            throws Exception {

        WebContext ctx = 
                new WebContext(request, response, servletContext, request.getLocale());

        templateEngine.process("home", ctx, response.getWriter());

    }

}
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.
public interface IContext {

    public Locale getLocale();
    public boolean containsVariable(final String name);
    public Set<String> getVariableNames();
    public Object getVariable(final String name);

}
There is a specialized extension of this interface, org.thymeleaf.context.IWebContext, meant to be used in ServletAPI-based web applications (like SpringMVC).
public interface IWebContext extends IContext {

    public HttpServletRequest getRequest();
    public HttpServletResponse getResponse();
    public HttpSession getSession();
    public ServletContext getServletContext();

}
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 ServletContextTemplateResolver requires that we use a context implementing IWebContext.
WebContext ctx = new WebContext(request, response, servletContext, request.getLocale());
Only three out of those four constructor arguments are required because the default locale for 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 a request attribute.
  • ${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 a servlet context attribute called x.

5.1.3.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:
templateEngine.process("home", ctx, response.getWriter());
Let’s see the results of this using the Spanish locale:


<html>

  <head>
    <title>Good Thymes Virtual Grocerytitle>
    <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
    <link rel="stylesheet" type="text/css" media="all" href="/gtvg/css/gtvg.css" />
  head>

  <body>

    <p>!Bienvenido a nuestra tienda de comestibles!p>

  body>

html>

5.2.3.2 More on texts and variables

5.2.1.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?
home.welcome=Welcome to our <b>fantastic</b> grocery store!
If we execute this template like before, we will obtain:
<p>Welcome to our <b>fantastic</b> grocery store!p>
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”):
<p th:utext="#{home.welcome}">Welcome to our grocery store!p>
This will output our message just like we wanted it:
<p>Welcome to our <b>fantasticb> grocery store!p>

5.2.2.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:
public void process(
            final HttpServletRequest request, final HttpServletResponse response,
            final ServletContext servletContext, final ITemplateEngine templateEngine)
            throws Exception {

    SimpleDateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy");
    Calendar cal = Calendar.getInstance();

    WebContext ctx = 
            new WebContext(request, response, servletContext, request.getLocale());
    ctx.setVariable("today", dateFormat.format(cal.getTime()));

    templateEngine.process("home", ctx, response.getWriter());

}
We have added a String variable called today to our context, and now we can display it in our template:
<body>

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

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

body>
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.

6.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:
<p th:utext="#{home.welcome}">Welcome to our grocery store!p>

<p>Today is: <span th:text="${today}">13 february 2011span>p>
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:
'User is of type ' + (${user.isAdmin()} ? 'Administrator' : (${user.type} ?: 'Unknown'))

6.1.4.1 Messages

As we already know, #{...} message expressions allow us to link this:
<p th:utext="#{home.welcome}">Welcome to our grocery store!p>
…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?
<p>!Bienvenido a nuestra tienda de comestibles, John Apricot!p>
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 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:
<p th:utext="#{home.welcome(${session.user.name})}">
  Welcome to our grocery store, Sebastian Pepper!
p>
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:
<p th:utext="#{${welcomeMsgKey}(${session.user.name})}">
  Welcome to our grocery store, Sebastian Pepper!
p>

6.2.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 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:
<p>Today is: <span th:text="${today}">13 february 2011span>.p>
…is in fact equivalent to this:
ctx.getVariable("today");
But OGNL allows us to create quite more powerful expressions, and that’s how this:
<p th:utext="#{home.welcome(${session.user.name})}">
  Welcome to our grocery store, Sebastian Pepper!
p>
…obtains the user name by executing:
((User) ctx.getVariable("session").get("user")).getName();
But getter method navigation is just one of OGNL’s features. Let’s see some more:
/*
 * 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('-')}

6.2.1.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.
  • #request: (only in Web Contexts) the HttpServletRequest object.
  • #response: (only in Web Contexts) the HttpServletResponse object.
  • #session: (only in Web Contexts) the HttpSession object.
  • #servletContext: (only in Web Contexts) the ServletContext object.
So we can do this:
Established locale country: <span th:text="${#locale.country}">USspan>.
You can read the full reference of these objects in Appendix A.

6.2.2.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.
  • #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.

6.2.3.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:
SimpleDateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy");
Calendar cal = Calendar.getInstance();

WebContext ctx = new WebContext(request, servletContext, request.getLocale());
ctx.setVariable("today", dateFormat.format(cal.getTime()));

templateEngine.process("home", ctx, response.getWriter());
…we can do just this:
WebContext ctx = 
    new WebContext(request, response, servletContext, request.getLocale());
ctx.setVariable("today", Calendar.getInstance());

templateEngine.process("home", ctx, response.getWriter());
…and then perform date formatting in the view layer itself:
<p>
  Today is: <span th:text="${#calendars.format(today,'dd MMMM yyyy')}">13 May 2011span>
p>

6.3.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:
  <div th:object="${session.user}">
    <p>Name: <span th:text="*{firstName}">Sebastianspan>.p>
    <p>Surname: <span th:text="*{lastName}">Pepperspan>.p>
    <p>Nationality: <span th:text="*{nationality}">Saturnspan>.p>
  div>
Which is exactly equivalent to:
<div>
  <p>Name: <span th:text="${session.user.firstName}">Sebastianspan>.p>
  <p>Surname: <span th:text="${session.user.lastName}">Pepperspan>.p>
  <p>Nationality: <span th:text="${session.user.nationality}">Saturnspan>.p>
div>
Of course, dollar and asterisk syntax can be mixed:
<div th:object="${session.user}">
  <p>Name: <span th:text="*{firstName}">Sebastianspan>.p>
  <p>Surname: <span th:text="${session.user.lastName}">Pepperspan>.p>
  <p>Nationality: <span th:text="*{nationality}">Saturnspan>.p>
div>
When an object selection is in place, the selected object will also be available to dollar expressions as the #object expression variable:
<div th:object="${session.user}">
  <p>Name: <span th:text="${#object.firstName}">Sebastianspan>.p>
  <p>Surname: <span th:text="${session.user.lastName}">Pepperspan>.p>
  <p>Nationality: <span th:text="*{nationality}">Saturnspan>.p>
div>
As said, if no object selection has been performed, dollar and asterisk syntaxes are equivalent.
<div>
  <p>Name: <span th:text="*{session.user.name}">Sebastianspan>.p>
  <p>Surname: <span th:text="*{session.user.surname}">Pepperspan>.p>
  <p>Nationality: <span th:text="*{session.user.nationality}">Saturnspan>.p>
div>

6.5.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):
<div th:insert="~{commons :: main}">...div>
But they can be used anywhere, just as any other variable:
<div th:with="frag=~{footer :: #main/text()}">
  <p th:insert="${frag}">
div>
Later in this tutorial there is an entire section devoted to Template Layout, including deeper explanation of fragment expressions.

6.6.4.6 Literals

6.6.1.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 \'.
<p>
  Now you are looking at a <span th:text="'working web application'">template filespan>.
p>

6.6.2.Number literals

Numeric literals are just that: numbers.
<p>The year is <span th:text="2013">1492span>.p>
<p>In two years, it will be <span th:text="2013 + 2">1494span>.p>

6.6.3.Boolean literals

The boolean literals are true and false. For example:
<div th:if="${user.isAdmin()} == false"> ...
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:
<div th:if="${user.isAdmin() == false}"> ...

6.6.4.The null literal

The null literal can be also used:
<div th:if="${variable.something} == null"> ...

6.6.5.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:
<div th:class="content">...div>
instead of:
<div th:class="'content'">...div>

6.7.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:
<span th:text="'The name of the user is ' + ${user.name}">

6.8.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:
<span th:text="|Welcome to our application, ${user.name}!|">
Which is equivalent to:
<span th:text="'Welcome to our application, ' + ${user.name} + '!'">
Literal substitutions can be combined with other types of expressions:
<span th:text="${onevar} + ' ' + |${twovar}, ${threevar}|">
Only variable/message expressions (${...}, *{...}, #{...}) are allowed inside |...| literal substitutions. No other literals ('...'), boolean/numeric tokens, conditional expressions etc. are.

6.9.4.9 Arithmetic operations

Some arithmetic operations are also available: +, -, *, / and %.
<div th:with="isEven=(${prodStat.count} % 2 == 0)">
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):
<div th:with="isEven=${prodStat.count % 2 == 0}">
Note that textual aliases exist for some of these operators: div (/), mod (%).

6.10.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 >.
<div th:if="${prodStat.count} > 1">
<span th:text="'Execution mode is ' + ( (${execMode} == 'dev')? 'Development' : 'Production')">
A simpler alternative may be using textual aliases that exist for some of these operators: gt (>), lt (<), ge (>=), le (<=), not (!). Also eq (==), neq/ne (!=).

6.11.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):
<tr th:class="${row.even}? 'even' : 'odd'">
  ...
tr>
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:
<tr th:class="${row.even}? (${row.first}? 'first' : 'even') : 'odd'">
  ...
tr>
Else expressions can also be omitted, in which case a null value is returned if the condition is false:
<tr th:class="${row.even}? 'alt'">
  ...
tr>

6.12.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:
<div th:object="${session.user}">
  ...
  <p>Age: <span th:text="*{age}?: '(no age specified)'">27span>.p>
div>
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:
<p>Age: <span th:text="*{age != null}? *{age} : '(no age specified)'">27span>.p>
As with conditional values, they can contain nested expressions between parentheses:
<p>
  Name: 
  <span th:text="*{firstName}?: (*{admin}? 'Admin' : #{default.username})">Sebastianspan>
p>

6.13.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:
<span th:text="${user.name} ?: 'no user authenticated'">...span>
…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:
<span th:text="${user.name} ?: _">no user authenticatedspan>

6.14.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:
<td th:text="${{user.lastAccessDate}}">...td>
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 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.

6.15.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:
article.text=@myapp.translator.Translator@translateToFrench({0})
…and a Messages_es.properties equivalent:
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:
<p th:text="${__#{article.text('textVar')}__}">Some text here...p>
Note that the preprocessing step for a French locale will be creating the following equivalent:
<p th:text="${@[email protected](textVar)}">Some text here...p>
The preprocessing String __ can be escaped in attributes using \_\_.

7.5 Setting Attribute Values

This chapter will explain the way in which we can set (or modify) values of attributes in our markup.

7.1.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:
<form action="subscribe.html">
  <fieldset>
    <input type="text" name="email" />
    <input type="submit" value="Subscribe!" />
  fieldset>
form>
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:
<form action="subscribe.html" th:attr="action=@{/subscribe}">
  <fieldset>
    <input type="text" name="email" />
    <input type="submit" value="Subscribe!" th:attr="value=#{subscribe.submit}"/>
  fieldset>
form>
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:
<form action="/gtvg/subscribe">
  <fieldset>
    <input type="text" name="email" />
    <input type="submit" value="!Suscríbe!"/>
  fieldset>
form>
Besides the new attribute values, you can also see that the applicacion 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:
<img src="../../images/gtvglogo.png" 
     th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />
Given the required messages files, this will output:
<img src="/gtgv/images/gtvglogo.png" title="Logo de Good Thymes" alt="Logo de Good Thymes" />

7.2.5.2 Setting value to specific attributes

By now, you might be thinking that something like:
<input type="submit" value="Subscribe!" th:attr="value=#{subscribe.submit}"/>
…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:
<input type="submit" value="Subscribe!" th:value="#{subscribe.submit}"/>
This looks much better! Let’s try and do the same to the action attribute in the form tag:
<form action="subscribe.html" th:action="@{/subscribe}">
And do you remember those th:href we put in our home.html before? They are exactly this same kind of attributes:
<li><a href="product/list.html" th:href="@{/product/list}">Product Lista>li>
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

7.3.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:
<img src="../../images/gtvglogo.png" 
     th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />
…or this, which is equivalent:
<img src="../../images/gtvglogo.png" 
     th:src="@{/images/gtvglogo.png}" th:title="#{logo}" th:alt="#{logo}" />
…with this:
<img src="../../images/gtvglogo.png" 
     th:src="@{/images/gtvglogo.png}" th:alt-title="#{logo}" />

7.4.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:
<input type="button" value="Do it!" class="btn" th:attrappend="class=${' ' + cssStyle}" />
If you process this template with the cssStyle variable set to "warning", you will get:
<input type="button" value="Do it!" class="btn warning" />
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:
<tr th:each="prod : ${prods}" class="row" th:classappend="${prodStat.odd}? 'odd'">
(Don’t worry about that th:each attribute. It is an iterating attribute and we will talk about it later.)

7.5.5.5 Fixed-value boolean attributes

HTML has the concept of boolean attributes, attributes that have no value and the prescence of one means that value is “true”. In XHTML, these attributes take just 1 value, which is itself. For example, checked:
<input type="checkbox" name="option2" checked /> 
<input type="checkbox" name="option1" checked="checked" /> 
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:
<input type="checkbox" name="active" th:checked="${user.active}" />
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

7.6.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:
<span th:whatever="${user.name}">...span>
Will result in:
<span whatever="John Apricot">...span>

7.7.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.
<table>
    <tr data-th-each="user : ${users}">
        <td data-th-text="${user.login}">...td>
        <td data-th-text="${user.name}">...td>
    tr>
table>
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.

8.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.

8.1.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.

8.1.1.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:
public void process(
        final HttpServletRequest request, final HttpServletResponse response,
        final ServletContext servletContext, final ITemplateEngine templateEngine)
        throws Exception {

    ProductService productService = new ProductService();
    List<Product> allProducts = productService.findAll(); 

    WebContext ctx = new WebContext(request, response, servletContext, request.getLocale());
    ctx.setVariable("prods", allProducts);

    templateEngine.process("product/list", ctx, response.getWriter());

}
And then we will use th:each in our template to iterate over the list of products:


<html xmlns:th="http://www.thymeleaf.org">

  <head>
    <title>Good Thymes Virtual Grocerytitle>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <link rel="stylesheet" type="text/css" media="all" 
          href="../../../css/gtvg.css" th:href="@{/css/gtvg.css}" />
  head>

  <body>

    <h1>Product listh1>

    <table>
      <tr>
        <th>NAMEth>
        <th>PRICEth>
        <th>IN STOCKth>
      tr>
      <tr th:each="prod : ${prods}">
        <td th:text="${prod.name}">Onionstd>
        <td th:text="${prod.price}">2.41td>
        <td th:text="${prod.inStock}? #{true} : #{false}">yestd>
      tr>
    table>

    <p>
      <a href="../home.html" th:href="@{/}">Return to homea>
    p>

  body>

html>
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 .

8.1.2.Iterable values

The java.util.List class isn’t the onlyvalue 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 array.
  • Any other object will be treated as if it were a single-valued list containing the object itself.

8.2.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:
<table>
  <tr>
    <th>NAMEth>
    <th>PRICEth>
    <th>IN STOCKth>
  tr>
  <tr th:each="prod,iterStat : ${prods}" th:class="${iterStat.odd}? 'odd'">
    <td th:text="${prod.name}">Onionstd>
    <td th:text="${prod.price}">2.41td>
    <td th:text="${prod.inStock}? #{true} : #{false}">yestd>
  tr>
table>
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>

  <head>
    <title>Good Thymes Virtual Grocerytitle>
    <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
    <link rel="stylesheet" type="text/css" media="all" href="/gtvg/css/gtvg.css" />
  head>

  <body>

    <h1>Product listh1>

    <table>
      <tr>
        <th>NAMEth>
        <th>PRICEth>
        <th>IN STOCKth>
      tr>
      <tr class="odd">
        <td>Fresh Sweet Basiltd>
        <td>4.99td>
        <td>yestd>
      tr>
      <tr>
        <td>Italian Tomatotd>
        <td>1.25td>
        <td>notd>
      tr>
      <tr class="odd">
        <td>Yellow Bell Peppertd>
        <td>2.50td>
        <td>yestd>
      tr>
      <tr>
        <td>Old Cheddartd>
        <td>18.75td>
        <td>yestd>
      tr>
    table>

    <p>
      <a href="/gtvg/" shape="rect">Return to homea>
    p>

  body>

html>
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:
<table>
  <tr>
    <th>NAMEth>
    <th>PRICEth>
    <th>IN STOCKth>
  tr>
  <tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'">
    <td th:text="${prod.name}">Onionstd>
    <td th:text="${prod.price}">2.41td>
    <td th:text="${prod.inStock}? #{true} : #{false}">yestd>
  tr>
table>

8.3.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:
context.setVariable(
     "users",
     new LazyContextVariable<List<User>>() {
         @Override
         protected List<User> loadValue() {
             return databaseRepository.findAllUsers();
         }
     });
This variable can be used without knowledge of its lazyness, in code such as:
<ul>
  <li th:each="u : ${users}" th:text="${u.name}">user nameli>
ul>
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:
<ul th:if="${condition}">
  <li th:each="u : ${users}" th:text="${u.name}">user nameli>
ul>

9.7 Conditional Evaluation

9.1.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:
<table>
  <tr>
    <th>NAMEth>
    <th>PRICEth>
    <th>IN STOCKth>
    <th>COMMENTSth>
  tr>
  <tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'">
    <td th:text="${prod.name}">Onionstd>
    <td th:text="${prod.price}">2.41td>
    <td th:text="${prod.inStock}? #{true} : #{false}">yestd>
    <td>
      <span th:text="${#lists.size(prod.comments)}">2span> comment/s
      <a href="comments.html" 
         th:href="@{/product/comments(prodId=${prod.id})}" 
         th:if="${not #lists.isEmpty(prod.comments)}">viewa>
    td>
  tr>
table>
Quite a lot of things to see here, so let’s focus on the important line:
<a href="comments.html"
   th:href="@{/product/comments(prodId=${prod.id})}" 
   th:if="${not #lists.isEmpty(prod.comments)}">viewa>
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:
<table>
  <tr>
    <th>NAMEth>
    <th>PRICEth>
    <th>IN STOCKth>
    <th>COMMENTSth>
  tr>
  <tr>
    <td>Fresh Sweet Basiltd>
    <td>4.99td>
    <td>yestd>
    <td>
      <span>0span> comment/s
    td>
  tr>
  <tr class="odd">
    <td>Italian Tomatotd>
    <td>1.25td>
    <td>notd>
    <td>
      <span>2span> comment/s
      <a href="/gtvg/product/comments?prodId=2">viewa>
    td>
  tr>
  <tr>
    <td>Yellow Bell Peppertd>
    <td>2.50td>
    <td>yestd>
    <td>
      <span>0span> comment/s
    td>
  tr>
  <tr class="odd">
    <td>Old Cheddartd>
    <td>18.75td>
    <td>yestd>
    <td>
      <span>1span> comment/s
      <a href="/gtvg/product/comments?prodId=4">viewa>
    td>
  tr>
table>
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:
<a href="comments.html"
   th:href="@{/comments(prodId=${prod.id})}" 
   th:unless="${#lists.isEmpty(prod.comments)}">viewa>

9.2.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.
<div th:switch="${user.role}">
  <p th:case="'admin'">User is an administratorp>
  <p th:case="#{roles.manager}">User is a managerp>
div>
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="*":
<div th:switch="${user.role}">
  <p th:case="'admin'">User is an administratorp>
  <p th:case="#{roles.manager}">User is a managerp>
  <p th:case="*">User is some other thingp>
div>

10.8 Template Layout

10.1.8.1 Including template fragments

10.1.1.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 xmlns:th="http://www.thymeleaf.org">

  <body>

    <div th:fragment="copy">
      (c) 2011 The Good Thymes Virtual Grocery
    div>

  body>

html>
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 (and also th:include, though its use is no longer recommended since Thymeleaf 3.0):
<body>

  ...

  <div th:insert="~{footer :: copy}">div>

body>
Note that th:insert expects a fragment expression (~{...}), which is an expression that results in a fragment. In the above example though, which is a non-complex fragment expression, the (~{,}) enclosing is completely optional, so the code above would be equivalent to:
<body>

  ...

  <div th:insert="footer :: copy">div>

body>

10.1.2.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 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:
<div th:insert="footer :: (${user.isAdmin}? #{footer.admin} : #{footer.normaluser})">div>
Note again how the surrounding ~{...} envelope is optional in th:insert/th:replace. 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.

10.1.3.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:
...
<div id="copy-section">
  (c) 2011 The Good Thymes Virtual Grocery
div>
...
We can use the fragment above simply referencing it by its id attribute, in a similar way to a CSS selector:
<body>

  ...

  <div th:insert="~{footer :: #copy-section}">div>

body>

10.1.4.Difference between th:insert and th:replace (and th:include)

And what is the difference between th:insert and th:replace (and th:include, not recommended since 3.0)?
  • th:insert is the simplest: it will simply insert the specified fragment as the body of its host tag.
  • th:replace actually replaces its host tag with the specified fragment.
  • th:include is similar to th:insert, but instead of inserting the fragment it only inserts the contents of this fragment.
So an HTML fragment like this:
<footer th:fragment="copy">
  (c) 2011 The Good Thymes Virtual Grocery
footer>
…included three times in host
tags, like this:
<body>

  ...

  <div th:insert="footer :: copy">div>

  <div th:replace="footer :: copy">div>

  <div th:include="footer :: copy">div>

body>
…will result in:
<body>

  ...

  <div>
    <footer>
      (c) 2011 The Good Thymes Virtual Grocery
    footer>
  div>

  <footer>
    (c) 2011 The Good Thymes Virtual Grocery
  footer>

  <div>
    (c) 2011 The Good Thymes Virtual Grocery
  div>

body>

10.2.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:
<div th:fragment="frag (onevar,twovar)">
    <p th:text="${onevar} + ' - ' + ${twovar}">...p>
div>
This requires the use of one of these two syntaxes to call the fragment from th:insert or th:replace:
<div th:replace="::frag (${value1},${value2})">...div>
<div th:replace="::frag (onevar=${value1},twovar=${value2})">...div>
Note that order is not important in the last option:
<div th:replace="::frag (twovar=${value2},onevar=${value1})">...div>

10.2.1.Fragment local variables without fragment arguments

Even if fragments are defined without arguments like this:
<div th:fragment="frag">
    ...
div>
We could use the second syntax specified above to call them (and only the second one):
<div th:replace="::frag (onevar=${value1},twovar=${value2})">
This would be equivalent to a combination of th:replace and th:with:
<div th:replace="::frag" th:with="onevar=${value1},twovar=${value2}">
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.

10.2.2.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.
<div th:assert="${onevar},(${twovar} != 43)">...div>
This comes in handy for validating parameters at a fragment signature:
<header th:fragment="contentheader(title)" th:assert="${!#strings.isEmpty(title)}">...header>

10.3.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:
<head th:fragment="common_header(title,links)">

  <title th:replace="${title}">The awesome applicationtitle>

  
  <link rel="stylesheet" type="text/css" media="all" th:href="@{/css/awesomeapp.css}">
  <link rel="shortcut icon" th:href="@{/images/favicon.ico}">
  <script type="text/javascript" th:src="@{/sh/scripts/codebase.js}">script>

  
  <th:block th:replace="${links}" />

head>
We can now call this fragment like:
...
<head th:replace="base :: common_header(~{::title},~{::link})">

  <title>Awesome - Maintitle>

  <link rel="stylesheet" th:href="@{/css/bootstrap.min.css}">
  <link rel="stylesheet" th:href="@{/themes/smoothness/jquery-ui.css}">

head>
...
…and the result will use the actual </code> and <code><link></code> tags from our calling template as the values of the <code>title</code> and <code>links</code> variables, resulting in our fragment being customized during insertion: <pre class=" language-html"><code class=" language-html">... <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>Awesome - Main<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span> <span class="token comment" spellcheck="true"><!-- Common styles and scripts --></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text/css<span class="token punctuation">"</span></span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>all<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/awe/css/awesomeapp.css<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>shortcut icon<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/awe/images/favicon.ico<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text/javascript<span class="token punctuation">"</span></span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/awe/sh/scripts/codebase.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/awe/css/bootstrap.min.css<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/awe/themes/smoothness/jquery-ui.css<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span> ...</code></pre> <section id="using-the-empty-fragment" class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="Using the empty fragment"><em>10.3.1.</em>Using the empty fragment</h3> A special fragment expression, the <em>empty fragment</em> (<code>~{}</code>), can be used for specifying <em>no markup</em>. Using the previous example: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span> <span class="token attr-name"><span class="token namespace">th:</span>replace</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>base :: common_header(~{::title},~{})<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>Awesome - Main<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span> ...</code></pre> Note how the second parameter of the fragment (<code>links</code>) is set to the <em>empty fragment</em> and therefore nothing is written for the <code><th:block th:replace="${links}" /></code> block: <pre class=" language-html"><code class=" language-html">... <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>Awesome - Main<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span> <span class="token comment" spellcheck="true"><!-- Common styles and scripts --></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text/css<span class="token punctuation">"</span></span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>all<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/awe/css/awesomeapp.css<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>shortcut icon<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/awe/images/favicon.ico<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text/javascript<span class="token punctuation">"</span></span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/awe/sh/scripts/codebase.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span> ...</code></pre> </section><section id="using-the-no-operation-token" class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="Using the no-operation token"><em>10.3.2.</em>Using the no-operation token</h3> 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 <code>common_header</code> example: <pre class=" language-html"><code class=" language-html">... <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span> <span class="token attr-name"><span class="token namespace">th:</span>replace</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>base :: common_header(_,~{::link})<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>Awesome - Main<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@{/css/bootstrap.min.css}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@{/themes/smoothness/jquery-ui.css}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span> ...</code></pre> See how the <code>title</code> argument (first argument of the <code>common_header</code> fragment) is set to <em>no-op</em> (<code>_</code>), which results in this part of the fragment not being executed at all (<code>title</code> = <em>no-operation</em>): <pre class=" language-html"><code class=" language-html"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span> <span class="token attr-name"><span class="token namespace">th:</span>replace</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${title}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>The awesome application<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span></code></pre> So the result is: <pre class=" language-html"><code class=" language-html">... <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>The awesome application<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span> <span class="token comment" spellcheck="true"><!-- Common styles and scripts --></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text/css<span class="token punctuation">"</span></span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>all<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/awe/css/awesomeapp.css<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>shortcut icon<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/awe/images/favicon.ico<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text/javascript<span class="token punctuation">"</span></span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/awe/sh/scripts/codebase.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/awe/css/bootstrap.min.css<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/awe/themes/smoothness/jquery-ui.css<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span> ...</code></pre> </section><section id="advanced-conditional-insertion-of-fragments" class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="Advanced conditional insertion of fragments"><em>10.3.3.</em>Advanced conditional insertion of fragments</h3> The availability of both the <em>emtpy fragment</em> and <em>no-operation token</em> 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 <code>common :: adminhead</code> fragment <em>only</em> if the user is an administrator, and insert nothing (emtpy fragment) if not: <pre class=" language-html"><code class=" language-html">... <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name"><span class="token namespace">th:</span>insert</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${user.isAdmin()} ? ~{common :: adminhead} : ~{}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span> ...</code></pre> Also, we can use the <em>no-operation token</em> 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: <pre class=" language-html"><code class=" language-html">... <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name"><span class="token namespace">th:</span>insert</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${user.isAdmin()} ? ~{common :: adminhead} : _<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> Welcome [[${user.name}]], click <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name"><span class="token namespace">th:</span>href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@{/support}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>here<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span> for help-desk support. <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span> ...</code></pre> Additionally, if we have configured our template resolvers to <em>check for existence</em> of the template resources –- by means of their <code>checkExistence</code> flag -– we can use the existence of the fragment itself as the condition in a <em>default</em> operation: <pre class=" language-html"><code class=" language-html">... <span class="token comment" spellcheck="true"><!-- The body of the <div> will be used if the "common :: salutation" fragment --></span> <span class="token comment" spellcheck="true"><!-- does not exist (or is empty). --></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name"><span class="token namespace">th:</span>insert</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>~{common :: salutation} ?: _<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> Welcome [[${user.name}]], click <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name"><span class="token namespace">th:</span>href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@{/support}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>here<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span> for help-desk support. <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span> ...</code></pre> </section></section><section id="removing-template-fragments" class="level2"> <h2 class="content-index-heading content-index-heading-level-2" name="" id="8.4 Removing template fragments"><em>10.4.</em>8.4 Removing template fragments</h2> Back to the example application, let’s revisit the last version of our product list template: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>table</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span><span class="token punctuation">></span></span>NAME<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span><span class="token punctuation">></span></span>PRICE<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span><span class="token punctuation">></span></span>IN STOCK<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span><span class="token punctuation">></span></span>COMMENTS<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span> <span class="token attr-name"><span class="token namespace">th:</span>each</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>prod : ${prods}<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${prodStat.odd}? <span class="token punctuation">'</span>odd<span class="token punctuation">'</span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${prod.name}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Onions<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${prod.price}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>2.41<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${prod.inStock}? #{true} : #{false}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>yes<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${#lists.size(prod.comments)}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> comment/s <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>comments.html<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@{/product/comments(prodId<span class="token punctuation">=</span>${prod.id})}<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>unless</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${#lists.isEmpty(prod.comments)}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>view<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>table</span><span class="token punctuation">></span></span></code></pre> 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, <em>we need more rows</em>. So let’s add some: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>table</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span><span class="token punctuation">></span></span>NAME<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span><span class="token punctuation">></span></span>PRICE<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span><span class="token punctuation">></span></span>IN STOCK<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span><span class="token punctuation">></span></span>COMMENTS<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span> <span class="token attr-name"><span class="token namespace">th:</span>each</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>prod : ${prods}<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${prodStat.odd}? <span class="token punctuation">'</span>odd<span class="token punctuation">'</span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${prod.name}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Onions<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${prod.price}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>2.41<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${prod.inStock}? #{true} : #{false}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>yes<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${#lists.size(prod.comments)}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> comment/s <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>comments.html<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@{/product/comments(prodId<span class="token punctuation">=</span>${prod.id})}<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>unless</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${#lists.isEmpty(prod.comments)}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>view<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>odd<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>Blue Lettuce<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>9.55<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>no<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>0<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> comment/s <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>Mild Cinnamon<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>1.99<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>yes<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>3<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> comment/s <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>comments.html<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>view<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>table</span><span class="token punctuation">></span></span></code></pre> Ok, now we have three, definitely better for a prototype. But… what will happen when we process it with Thymeleaf?: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>table</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span><span class="token punctuation">></span></span>NAME<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span><span class="token punctuation">></span></span>PRICE<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span><span class="token punctuation">></span></span>IN STOCK<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span><span class="token punctuation">></span></span>COMMENTS<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>Fresh Sweet Basil<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>4.99<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>yes<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>0<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> comment/s <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>odd<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>Italian Tomato<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>1.25<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>no<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> comment/s <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/gtvg/product/comments?prodId<span class="token punctuation">=</span>2<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>view<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>Yellow Bell Pepper<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>2.50<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>yes<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>0<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> comment/s <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>odd<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>Old Cheddar<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>18.75<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>yes<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>1<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> comment/s <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/gtvg/product/comments?prodId<span class="token punctuation">=</span>4<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>view<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>odd<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>Blue Lettuce<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>9.55<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>no<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>0<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> comment/s <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>Mild Cinnamon<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>1.99<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>yes<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>3<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> comment/s <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>comments.html<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>view<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>table</span><span class="token punctuation">></span></span></code></pre> 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 <code>th:remove</code> attribute on the second and third <code><tr></code> tags: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>table</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span><span class="token punctuation">></span></span>NAME<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span><span class="token punctuation">></span></span>PRICE<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span><span class="token punctuation">></span></span>IN STOCK<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span><span class="token punctuation">></span></span>COMMENTS<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span> <span class="token attr-name"><span class="token namespace">th:</span>each</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>prod : ${prods}<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${prodStat.odd}? <span class="token punctuation">'</span>odd<span class="token punctuation">'</span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${prod.name}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Onions<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${prod.price}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>2.41<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${prod.inStock}? #{true} : #{false}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>yes<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${#lists.size(prod.comments)}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> comment/s <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>comments.html<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@{/product/comments(prodId<span class="token punctuation">=</span>${prod.id})}<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>unless</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${#lists.isEmpty(prod.comments)}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>view<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>odd<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>remove</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>all<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>Blue Lettuce<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>9.55<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>no<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>0<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> comment/s <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span> <span class="token attr-name"><span class="token namespace">th:</span>remove</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>all<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>Mild Cinnamon<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>1.99<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>yes<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>3<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> comment/s <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>comments.html<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>view<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>table</span><span class="token punctuation">></span></span></code></pre> Once processed, everything will look again as it should: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>table</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span><span class="token punctuation">></span></span>NAME<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span><span class="token punctuation">></span></span>PRICE<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span><span class="token punctuation">></span></span>IN STOCK<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span><span class="token punctuation">></span></span>COMMENTS<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>Fresh Sweet Basil<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>4.99<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>yes<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>0<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> comment/s <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>odd<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>Italian Tomato<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>1.25<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>no<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> comment/s <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/gtvg/product/comments?prodId<span class="token punctuation">=</span>2<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>view<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>Yellow Bell Pepper<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>2.50<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>yes<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>0<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> comment/s <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>odd<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>Old Cheddar<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>18.75<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>yes<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>1<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> comment/s <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/gtvg/product/comments?prodId<span class="token punctuation">=</span>4<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>view<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>table</span><span class="token punctuation">></span></span></code></pre> And what does that <code>all</code> value in the attribute, mean? <code>th:remove</code> can behave in five different ways, depending on its value: <ul> <li> <code>all</code>: Remove both the containing tag and all its children.</li> <li> <code>body</code>: Do not remove the containing tag, but remove all its children.</li> <li> <code>tag</code>: Remove the containing tag, but do not remove its children.</li> <li> <code>all-but-first</code>: Remove all children of the containing tag except the first one.</li> <li> <code>none</code> : Do nothing. This value is useful for dynamic evaluation.</li> </ul> What can that <code>all-but-first</code> value be useful for? It will let us save some <code>th:remove="all"</code> when prototyping: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>table</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>thead</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span><span class="token punctuation">></span></span>NAME<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span><span class="token punctuation">></span></span>PRICE<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span><span class="token punctuation">></span></span>IN STOCK<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span><span class="token punctuation">></span></span>COMMENTS<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>thead</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tbody</span> <span class="token attr-name"><span class="token namespace">th:</span>remove</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>all-but-first<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span> <span class="token attr-name"><span class="token namespace">th:</span>each</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>prod : ${prods}<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${prodStat.odd}? <span class="token punctuation">'</span>odd<span class="token punctuation">'</span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${prod.name}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Onions<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${prod.price}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>2.41<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${prod.inStock}? #{true} : #{false}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>yes<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${#lists.size(prod.comments)}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> comment/s <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>comments.html<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@{/product/comments(prodId<span class="token punctuation">=</span>${prod.id})}<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>unless</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${#lists.isEmpty(prod.comments)}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>view<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>odd<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>Blue Lettuce<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>9.55<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>no<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>0<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> comment/s <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>Mild Cinnamon<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>1.99<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span>yes<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>3<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> comment/s <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>comments.html<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>view<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tbody</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>table</span><span class="token punctuation">></span></span></code></pre> The <code>th:remove</code> attribute can take any <em>Thymeleaf Standard Expression</em>, as long as it returns one of the allowed String values (<code>all</code>, <code>tag</code>, <code>body</code>, <code>all-but-first</code> or <code>none</code>). This means removals could be conditional, like: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/something<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>remove</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${condition}? tag : none<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Link text not to be removed<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></code></pre> Also note that <code>th:remove</code> considers <code>null</code> a synonym to <code>none</code>, so the following works the same as the example above: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/something<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>remove</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${condition}? tag<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Link text not to be removed<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></code></pre> In this case, if <code>${condition}</code> is false, <code>null</code> will be returned, and thus no removal will be performed. </section><section id="layout-inheritance" class="level2"> <h2 class="content-index-heading content-index-heading-level-2" name="" id="8.5 Layout Inheritance"><em>10.5.</em>8.5 Layout Inheritance</h2> To be able to have a single file as layout, fragments can be used. An example of a simple layout having <code>title</code> and <code>content</code> using <code>th:fragment</code> and <code>th:replace</code>: <pre class=" language-html"><code class=" language-html"><span class="token doctype"><!DOCTYPE html></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name"><span class="token namespace">th:</span>fragment</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>layout (title, content)<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">xmlns:</span>th</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://www.thymeleaf.org<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span> <span class="token attr-name"><span class="token namespace">th:</span>replace</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${title}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Layout Title<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>Layout H1<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name"><span class="token namespace">th:</span>replace</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${content}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Layout content<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>footer</span><span class="token punctuation">></span></span> Layout footer <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>footer</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></code></pre> This example declares a fragment called <strong>layout</strong> having <em>title</em> and <em>content</em> as parameters. Both will be replaced on page inheriting it by provided fragment expressions in the example below. <pre class=" language-html"><code class=" language-html"><span class="token doctype"><!DOCTYPE html></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name"><span class="token namespace">th:</span>replace</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>~{layoutFile :: layout(~{::title}, ~{::section})}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>Page Title<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Page content<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span>Included on page<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></code></pre> In this file, the <code>html</code> tag will be replaced by <em>layout</em>, but in the layout <code>title</code> and <code>content</code> will have been replaced by <code>title</code> and <code>section</code> blocks respectively. If desired, the layout can be composed by several fragments as <em>header</em> and <em>footer</em>. </section></section><section id="local-variables" class="level1"> <h1 class="content-index-heading content-index-heading-level-1" name="" id="9 Local Variables"><em>11.</em>9 Local Variables</h1> Thymeleaf calls <em>local variables</em> 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 <code>prod</code> iter variable in our product list page: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span> <span class="token attr-name"><span class="token namespace">th:</span>each</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>prod : ${prods}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span></code></pre> That <code>prod</code> variable will be available only within the bounds of the <code><tr></code> tag. Specifically: <ul> <li>It will be available for any other <code>th:*</code> attributes executing in that tag with less <em>precedence</em> than <code>th:each</code> (which means they will execute after <code>th:each</code>).</li> <li>It will be available for any child element of the <code><tr></code> tag, such as any <code><td></code> elements.</li> </ul> Thymeleaf offers you a way to declare local variables without iteration, using the <code>th:with</code> attribute, and its syntax is like that of attribute value assignments: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name"><span class="token namespace">th:</span>with</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>firstPer<span class="token punctuation">=</span>${persons[0]}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span> The name of the first person is <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${firstPer.name}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Julius Caesar<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>. <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre> When <code>th:with</code> is processed, that <code>firstPer</code> 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 <code><div></code> tag. You can define several variables at the same time using the usual multiple assignment syntax: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name"><span class="token namespace">th:</span>with</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>firstPer<span class="token punctuation">=</span>${persons[0]},secondPer<span class="token punctuation">=</span>${persons[1]}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span> The name of the first person is <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${firstPer.name}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Julius Caesar<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>. <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span> But the name of the second person is <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${secondPer.name}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Marcus Antonius<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>. <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre> The <code>th:with</code> attribute allows reusing variables defined in the same attribute: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name"><span class="token namespace">th:</span>with</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>company<span class="token punctuation">=</span>${user.company + <span class="token punctuation">'</span> Co.<span class="token punctuation">'</span>},account<span class="token punctuation">=</span>${accounts[company]}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre> Let’s use this in our Grocery’s home page! Remember the code we wrote for outputting a formatted date? <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span> Today is: <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${#calendars.format(today,<span class="token punctuation">'</span>dd MMMM yyyy<span class="token punctuation">'</span>)}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>13 february 2011<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></code></pre> Well, what if we wanted that <code>"dd MMMM yyyy"</code> to actually depend on the locale? For example, we might want to add the following message to our <code>home_en.properties</code>: <pre class="prettyprint"><code>date.format=MMMM dd'','' yyyy</code></pre> …and an equivalent one to our <code>home_es.properties</code>: <pre class="prettyprint"><code>date.format=dd ''de'' MMMM'','' yyyy</code></pre> Now, let’s use <code>th:with</code> to get the localized date format into a variable, and then use it in our <code>th:text</code> expression: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span> <span class="token attr-name"><span class="token namespace">th:</span>with</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>df<span class="token punctuation">=</span>#{date.format}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> Today is: <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${#calendars.format(today,df)}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>13 February 2011<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></code></pre> That was clean and easy. In fact, given the fact that <code>th:with</code> has a higher <code>precedence</code> than <code>th:text</code>, we could have solved this all in the <code>span</code> tag: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span> Today is: <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name"><span class="token namespace">th:</span>with</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>df<span class="token punctuation">=</span>#{date.format}<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${#calendars.format(today,df)}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>13 February 2011<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></code></pre> 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. </section><section id="attribute-precedence" class="level1"> <h1 class="content-index-heading content-index-heading-level-1" name="" id="10 Attribute Precedence"><em>12.</em>10 Attribute Precedence</h1> What happens when you write more than one <code>th:*</code> attribute in the same tag? For example: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span> <span class="token attr-name"><span class="token namespace">th:</span>each</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>item : ${items}<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${item.description}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Item description here...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></code></pre> We would expect that <code>th:each</code> attribute to execute before the <code>th:text</code> 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 <em>precedence</em> 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: <div class="table-scroller"> <table style="width: 92%;"> <colgroup> <col style="width: 11%;"> <col style="width: 48%;"> <col style="width: 31%;"> </colgroup> <thead> <tr class="header"> <th style="text-align: left;">Order</th> <th style="text-align: left;">Feature</th> <th style="text-align: left;">Attributes</th> </tr> </thead> <tbody> <tr class="odd"> <td style="text-align: left;">1</td> <td style="text-align: left;">Fragment inclusion</td> <td style="text-align: left;"> <code>th:insert</code> <code>th:replace</code> </td> </tr> <tr class="even"> <td style="text-align: left;">2</td> <td style="text-align: left;">Fragment iteration</td> <td style="text-align: left;"><code>th:each</code></td> </tr> <tr class="odd"> <td style="text-align: left;">3</td> <td style="text-align: left;">Conditional evaluation</td> <td style="text-align: left;"> <code>th:if</code> <code>th:unless</code> <code>th:switch</code> <code>th:case</code> </td> </tr> <tr class="even"> <td style="text-align: left;">4</td> <td style="text-align: left;">Local variable definition</td> <td style="text-align: left;"> <code>th:object</code> <code>th:with</code> </td> </tr> <tr class="odd"> <td style="text-align: left;">5</td> <td style="text-align: left;">General attribute modification</td> <td style="text-align: left;"> <code>th:attr</code> <code>th:attrprepend</code> <code>th:attrappend</code> </td> </tr> <tr class="even"> <td style="text-align: left;">6</td> <td style="text-align: left;">Specific attribute modification</td> <td style="text-align: left;"> <code>th:value</code> <code>th:href</code> <code>th:src</code> <code>...</code> </td> </tr> <tr class="odd"> <td style="text-align: left;">7</td> <td style="text-align: left;">Text (tag body modification)</td> <td style="text-align: left;"> <code>th:text</code> <code>th:utext</code> </td> </tr> <tr class="even"> <td style="text-align: left;">8</td> <td style="text-align: left;">Fragment specification</td> <td style="text-align: left;"><code>th:fragment</code></td> </tr> <tr class="odd"> <td style="text-align: left;">9</td> <td style="text-align: left;">Fragment removal</td> <td style="text-align: left;"><code>th:remove</code></td> </tr> </tbody> </table> </div> 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): <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${item.description}<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>each</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>item : ${items}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Item description here...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></code></pre> </section><section id="comments-and-blocks" class="level1"> <h1 class="content-index-heading content-index-heading-level-1" name="" id="11 Comments and Blocks"><em>13.</em>11 Comments and Blocks</h1> <section id="standard-htmlxml-comments" class="level2"> <h2 class="content-index-heading content-index-heading-level-2" name="" id="11.1. Standard HTML/XML comments"><em>13.1.</em>11.1. Standard HTML/XML comments</h2> Standard HTML/XML comments <code><!-- ... --></code> can be used anywhere in Thymeleaf templates. Anything inside these comments won’t be processed by Thymeleaf, and will be copied verbatim to the result: <pre class=" language-html"><code class=" language-html"><span class="token comment" spellcheck="true"><!-- User info follows --></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${...}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre> </section><section id="thymeleaf-parser-level-comment-blocks" class="level2"> <h2 class="content-index-heading content-index-heading-level-2" name="" id="11.2. Thymeleaf parser-level comment blocks"><em>13.2.</em>11.2. Thymeleaf parser-level comment blocks</h2> Parser-level comment blocks are code that will be simply removed from the template when Thymeleaf parses it. They look like this: <pre class=" language-html"><code class=" language-html"><span class="token comment" spellcheck="true"><!--/* This code will be removed at Thymeleaf parsing time! */--></span></code></pre> Thymeleaf will remove everything between <code><!--/*</code> and <code>*/--></code>, 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: <pre class=" language-html"><code class=" language-html"><span class="token comment" spellcheck="true"><!--/*--></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span> you can see me only before Thymeleaf processes me! <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span> <span class="token comment" spellcheck="true"><!--*/--></span></code></pre> This might come very handy for prototyping tables with a lot of <code><tr></code>’s, for example: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>table</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span> <span class="token attr-name"><span class="token namespace">th:</span>each</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>x : ${xs}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token comment" spellcheck="true"><!--/*--></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token comment" spellcheck="true"><!--*/--></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>table</span><span class="token punctuation">></span></span></code></pre> </section><section id="thymeleaf-prototype-only-comment-blocks" class="level2"> <h2 class="content-index-heading content-index-heading-level-2" name="" id="11.3. Thymeleaf prototype-only comment blocks"><em>13.3.</em>11.3. Thymeleaf prototype-only comment blocks</h2> 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. <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>hello!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> <span class="token comment" spellcheck="true"><!--/*/ <div th:text="${...}"> ... </div> /*/--></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>goodbye!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></code></pre> Thymeleaf’s parsing system will simply remove the <code><!--/*/</code> and <code>/*/--></code> markers, but not its contents, which will be left therefore uncommented. So when executing the template, Thymeleaf will actually see this: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>hello!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${...}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>goodbye!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></code></pre> As with parser-level comment blocks, this feature is dialect-independent. </section><section id="synthetic-thblock-tag" class="level2"> <h2 class="content-index-heading content-index-heading-level-2" name="" id="11.4. Synthetic th:block tag"><em>13.4.</em>11.4. Synthetic <code>th:block</code> tag</h2> Thymeleaf’s only element processor (not an attribute) included in the Standard Dialects is <code>th:block</code>. <code>th:block</code> 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 <code><tr></code> for each element: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>table</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span><span class="token namespace">th:</span>block</span> <span class="token attr-name"><span class="token namespace">th:</span>each</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>user : ${users}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${user.login}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${user.name}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name">colspan</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>2<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${user.address}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span><span class="token namespace">th:</span>block</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>table</span><span class="token punctuation">></span></span></code></pre> And especially useful when used in combination with prototype-only comment blocks: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>table</span><span class="token punctuation">></span></span> <span class="token comment" spellcheck="true"><!--/*/ <th:block th:each="user : ${users}"> /*/--></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${user.login}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${user.name}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name">colspan</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>2<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${user.address}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token comment" spellcheck="true"><!--/*/ </th:block> /*/--></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>table</span><span class="token punctuation">></span></span></code></pre> Note how this solution allows templates to be valid HTML (no need to add forbidden <code><div></code> blocks inside <code><table></code>), and still works OK when open statically in browsers as prototypes! </section></section><section id="inlining" class="level1"> <h1 class="content-index-heading content-index-heading-level-1" name="" id="12 Inlining"><em>14.</em>12 Inlining</h1> <section id="expression-inlining" class="level2"> <h2 class="content-index-heading content-index-heading-level-2" name="" id="12.1 Expression inlining"><em>14.1.</em>12.1 Expression inlining</h2> 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: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Hello, [[${session.user.name}]]!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></code></pre> …instead of this: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Hello, <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${session.user.name}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Sebastian<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></code></pre> Expressions between <code>[[...]]</code> or <code>[(...)]</code> are considered <strong>inlined expressions</strong> in Thymeleaf, and inside them we can use any kind of expression that would also be valid in a <code>th:text</code> or <code>th:utext</code> attribute. Note that, while <code>[[...]]</code> corresponds to <code>th:text</code> (i.e. result will be <em>HTML-escaped</em>), <code>[(...)]</code> corresponds to <code>th:utext</code> and will not perform any HTML-escaping. So with a variable such as <code>msg = 'This is <b>great!</b>'</code>, given this fragment: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>The message is "[(${msg})]"<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></code></pre> The result will have those <code><b></code> tags unescaped, so: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>The message is "This is <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>b</span><span class="token punctuation">></span></span>great!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>b</span><span class="token punctuation">></span></span>"<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></code></pre> Whereas if escaped like: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>The message is "[[${msg}]]"<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></code></pre> The result will be HTML-escaped: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>The message is "This is <span class="token entity" title="<"><</span>b<span class="token entity" title=">">></span>great!<span class="token entity" title="<"><</span>/b<span class="token entity" title=">">></span>"<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></code></pre> Note that <strong>text inlining is active by default</strong> in the body of every tag in our markup –- not the tags themselves -–, so there is nothing we need to do to enable it. <section id="inlining-vs-natural-templates" class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="Inlining vs natural templates"><em>14.1.1.</em>Inlining vs natural templates</h3> If you come from other template engines in which this way of outputting text is the norm, you might be asking: <em>Why aren’t we doing this from the beginning? It’s less code than all those</em> <code>th:text</code> <em>attributes!</em> 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… <pre class="prettyprint"><code>Hello, Sebastian!</code></pre> …and using it… <pre class="prettyprint"><code>Hello, [[${session.user.name}]]!</code></pre> …is quite clear in terms of design usefulness. </section><section id="disabling-inlining" class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="Disabling inlining"><em>14.1.2.</em>Disabling inlining</h3> This mechanism can be disabled though, because there might actually be occasions in which we do want to output the <code>[[...]]</code> or <code>[(...)]</code> sequences without its contents being processed as an expression. For that, we will use <code>th:inline="none"</code>: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span> <span class="token attr-name"><span class="token namespace">th:</span>inline</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>none<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>A double array looks like this: [[1, 2, 3], [4, 5]]!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></code></pre> This will result in: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>A double array looks like this: [[1, 2, 3], [4, 5]]!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></code></pre> </section></section><section id="text-inlining" class="level2"> <h2 class="content-index-heading content-index-heading-level-2" name="" id="12.2 Text inlining"><em>14.2.</em>12.2 Text inlining</h2> <em>Text inlining</em> is very similar to the <em>expression inlining</em> capability we have just seen, but it actually adds more power. It has to be enabled explicitly with <code>th:inline="text"</code>. Text inlining not only allows us to use the same <em>inlined expressions</em> we just saw, but in fact processes <em>tag bodies</em> as if they were templates processed in the <code>TEXT</code> 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 <em>textual template modes</em>. </section><section id="javascript-inlining" class="level2"> <h2 class="content-index-heading content-index-heading-level-2" name="" id="12.3 JavaScript inlining"><em>14.3.</em>12.3 JavaScript inlining</h2> JavaScript inlining allows for a better integration of JavaScript <code><script></code> blocks in templates being processed in the <code>HTML</code> template mode. As with <em>text inlining</em>, this is actually equivalent to processing the scripts contents as if they were templates in the <code>JAVASCRIPT</code> template mode, and therefore all the power of the <em>textual template modes</em> (see next chapter) will be at hand. However, in this section we will focus on how we can use it for adding the output of our Thymeleaf expressions into our JavaScript blocks. This mode has to be explicitly enabled using <code>th:inline="javascript"</code>: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name"><span class="token namespace">th:</span>inline</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>javascript<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ... var username = [[${session.user.name}]]; ... <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre> This will result in: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name"><span class="token namespace">th:</span>inline</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>javascript<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ... var username = "Sebastian \"Fruity\" Applejuice"; ... <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre> Two important things to note in the code above: <em>First</em>, 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 <strong>well-formed JavaScript literal</strong>. <em>Second</em>, that this is happening because we are outputting the <code>${session.user.name}</code> expression as <strong>escaped</strong>, i.e. using a double-bracket expression: <code>[[${session.user.name}]]</code>. If instead we used <em>unescaped</em> like: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name"><span class="token namespace">th:</span>inline</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>javascript<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ... var username = [(${session.user.name})]; ... <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre> The result would look like: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name"><span class="token namespace">th:</span>inline</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>javascript<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ... var username = Sebastian "Fruity" Applejuice; ... <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre> …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. <section id="javascript-natural-templates" class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="JavaScript natural templates"><em>14.3.1.</em>JavaScript natural templates</h3> The mentioned <em>intelligence</em> 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: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name"><span class="token namespace">th:</span>inline</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>javascript<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ... var username = /*[[${session.user.name}]]*/ "Gertrud Kiwifruit"; ... <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre> And Thymeleaf will ignore everything we have written <em>after the comment and before the semicolon</em> (in this case <code>'Gertrud Kiwifruit'</code>), so the result of executing this will look exactly like when we were not using the wrapping comments: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name"><span class="token namespace">th:</span>inline</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>javascript<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ... var username = "Sebastian \"Fruity\" Applejuice"; ... <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre> But have another careful look at the original template code: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name"><span class="token namespace">th:</span>inline</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>javascript<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ... var username = /*[[${session.user.name}]]*/ "Gertrud Kiwifruit"; ... <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre> Note how this is <strong>valid JavaScript</strong> 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 <strong>JavaScript natural templates</strong>! </section><section id="advanced-inlined-evaluation-and-javascript-serialization" class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="Advanced inlined evaluation and JavaScript serialization"><em>14.3.2.</em>Advanced inlined evaluation and JavaScript serialization</h3> 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: <ul> <li>Strings</li> <li>Numbers</li> <li>Booleans</li> <li>Arrays</li> <li>Collections</li> <li>Maps</li> <li>Beans (objects with <em>getter</em> and <em>setter</em> methods)</li> </ul> For example, if we had the following code: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name"><span class="token namespace">th:</span>inline</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>javascript<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ... var user = /*[[${session.user}]]*/ null; ... <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre> That <code>${session.user}</code> expression will evaluate to a <code>User</code> object, and Thymeleaf will correctly convert it to Javascript syntax: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name"><span class="token namespace">th:</span>inline</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>javascript<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ... var user = {"age":null,"firstName":"John","lastName":"Apricot", "name":"John Apricot","nationality":"Antarctica"}; ... <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre> The way this JavaScript serialization is done is by means of an implementation of the <code>org.thymeleaf.standard.serializer.IStandardJavaScriptSerializer</code> interface, which can be configured at the instance of the <code>StandardDialect</code> being used at the template engine. The default implementation of this JS serialization mechanism will look for the <a href="http://www.75271.com/go.html/" target="_blank" rel="nofollow">Jackson library</a> 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). </section></section><section id="css-inlining" class="level2"> <h2 class="content-index-heading content-index-heading-level-2" name="" id="12.4 CSS inlining"><em>14.4.</em>12.4 CSS inlining</h2> Thymeleaf also allows the use of inlining in CSS <code><style></code> tags, such as: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span> <span class="token attr-name"><span class="token namespace">th:</span>inline</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>css<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span></code></pre> For example, say we have two variables set to two different <code>String</code> values: <pre class="prettyprint"><code>classname = 'main elems' align = 'center'</code></pre> We could use them just like: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span> <span class="token attr-name"><span class="token namespace">th:</span>inline</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>css<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> .[[${classname}]] { text-align: [[${align}]]; } <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span></code></pre> And the result would be: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span> <span class="token attr-name"><span class="token namespace">th:</span>inline</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>css<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> .main\ elems { text-align: center; } <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span></code></pre> Note how CSS inlining also bears some <em>intelligence</em>, just like JavaScript’s. Specifically, expressions output via <em>escaped</em> expressions like <code>[[${classname}]]</code> will be escaped as <strong>CSS identifiers</strong>. That is why our <code>classname = 'main elems'</code> has turned into <code>main\ elems</code> in the fragment of code above. <section id="advanced-features-css-natural-templates-etc." class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="Advanced features: CSS natural templates, etc."><em>14.4.1.</em>Advanced features: CSS natural templates, etc.</h3> In an equivalent way to what was explained before for JavaScript, CSS inlining also allows for our <code><style></code> tags to work both statically and dynamically, i.e. as <strong>CSS natural templates</strong> by means of wrapping inlined expressions in comments. See: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span> <span class="token attr-name"><span class="token namespace">th:</span>inline</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>css<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> .main\ elems { text-align: /*[[${align}]]*/ left; } <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span></code></pre> </section></section></section><section id="textual-template-modes" class="level1"> <h1 class="content-index-heading content-index-heading-level-1" name="" id="13 Textual template modes"><em>15.</em>13 Textual template modes</h1> <section id="textual-syntax" class="level2"> <h2 class="content-index-heading content-index-heading-level-2" name="" id="13.1 Textual syntax"><em>15.1.</em>13.1 Textual syntax</h2> Three of the Thymeleaf <em>template modes</em> are considered <strong>textual</strong>: <code>TEXT</code>, <code>JAVASCRIPT</code> and <code>CSS</code>. This differentiates them from the markup template modes: <code>HTML</code> and <code>XML</code>. The key difference between <em>textual</em> 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 <strong>inlining</strong>, 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. <pre class="prettyprint"><code> Dear [(${name})], Please find attached the results of the report you requested with name "[(${report.name})]". Sincerely, The Reporter.</code></pre> Even without tags, the example above is a complete and valid Thymeleaf template that can be executed in the <code>TEXT</code> template mode. But in order to include more complex logic than mere <em>output expressions</em>, we need a new non-tag-based syntax: <pre class="prettyprint"><code>[# th:each="item : ${items}"] - [(${item})] [/]</code></pre> Which is actually the <em>condensed</em> version of the more verbose: <pre class="prettyprint"><code>[#th:block th:each="item : ${items}"] - [#th:block th:utext="${item}" /] [/th:block]</code></pre> Note how this new syntax is based on elements (i.e. processable tags) that are declared as <code>[#element ...]</code> instead of <code><element ...></code>. Elements are open like <code>[#element ...]</code> and closed like <code>[/element]</code>, and standalone tags can be declared by minimizing the open element with a <code>/</code> in a way almost equivalent to XML tags: <code>[#element ... /]</code>. The Standard Dialect only contains a processor for one of these elements: the already-known <code>th:block</code>, though we could extend this in our dialects and create new elements in the usual way. Also, the <code>th:block</code> element (<code>[#th:block ...] ... [/th:block]</code>) is allowed to be abbreviated as the empty string (<code>[# ...] ... [/]</code>), so the above block is actually equivalent to: <pre class="prettyprint"><code>[# th:each="item : ${items}"] - [# th:utext="${item}" /] [/]</code></pre> And given <code>[# th:utext="${item}" /]</code> is equivalent to an <em>inlined unescaped expression</em>, we could just use it in order to have less code. Thus we end up with the first fragment of code we saw above: <pre class="prettyprint"><code>[# th:each="item : ${items}"] - [(${item})] [/]</code></pre> Note that the <em>textual syntax requires full element balance (no unclosed tags) and quoted attributes</em> – it’s more XML-style than HTML-style. Let’s have a look at a more complete example of a <code>TEXT</code> template, a <em>plain text</em> email template: <pre class="prettyprint"><code>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</code></pre> After executing, the result of this could be something like: <pre class="prettyprint"><code>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</code></pre> And another example in <code>JAVASCRIPT</code> template mode, a <code>greeter.js</code> file, we process as a textual template and which result we call from our HTML pages. Note this is <em>not</em> a <code><script></code> block in an HTML template, but a <code>.js</code> file being processed as a template on its own: <pre class=" language-javascript"><code class=" language-javascript">var greeter = function() { var username = [[${session.user.name}]]; [# th:each="salut : ${salutations}"] alert([[${salut}]] + " " + username); [/] };</code></pre> After executing, the result of this could be something like: <pre class=" language-javascript"><code class=" language-javascript">var greeter = function() { var username = "Bertrand \"Crunchy\" Pear"; alert("Hello" + " " + username); alert("Ol\u00E1" + " " + username); alert("Hola" + " " + username); };</code></pre> <section id="escaped-element-attributes" class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="Escaped element attributes"><em>15.1.1.</em>Escaped element attributes</h3> In order to avoid interactions with parts of the template that might be processed in other modes (e.g. <code>text</code>-mode inlining inside an <code>HTML</code> template), Thymeleaf 3.0 allows the attributes in elements in its <em>textual syntax</em> to be escaped. So: <ul> <li>Attributes in <code>TEXT</code> template mode will be <em>HTML-unescaped</em>.</li> <li>Attributes in <code>JAVASCRIPT</code> template mode will be <em>JavaScript-unescaped</em>.</li> <li>Attributes in <code>CSS</code> template mode will be <em>CSS-unescaped</em>.</li> </ul> So this would be perfectly OK in a <code>TEXT</code>-mode template (note the <code>></code>): <pre class="prettyprint"><code> [# th:if="${120<user.age}"] Congratulations! [/]</code></pre> Of course that <code><</code> would make no sense in a <em>real text</em> template, but it is a good idea if we are processing an HTML template with a <code>th:inline="text"</code> block containing the code above and we want to make sure our browser doesn’t take that <code><user.age</code> for the name of an open tag when statically opening the file as a prototype. </section></section><section id="extensibility" class="level2"> <h2 class="content-index-heading content-index-heading-level-2" name="" id="13.2 Extensibility"><em>15.2.</em>13.2 Extensibility</h2> One of the advantages of this syntax is that it is just as extensible as the <em>markup</em> one. Developers can still define their own dialects with custom elements and attributes, apply a prefix to them (optionally), and then use them in textual template modes: <pre class="prettyprint"><code> [#myorg:dosomething myorg:importantattr="211"]some text[/myorg:dosomething]</code></pre> </section><section id="textual-prototype-only-comment-blocks-adding-code" class="level2"> <h2 class="content-index-heading content-index-heading-level-2" name="" id="13.3 Textual prototype-only comment blocks: adding code"><em>15.3.</em>13.3 Textual prototype-only comment blocks: adding code</h2> The <code>JAVASCRIPT</code> and <code>CSS</code> template modes (not available for <code>TEXT</code>) allow including code between a special comment syntax <code>/*[+...+]*/</code> so that Thymeleaf will automatically uncomment such code when processing the template: <pre class=" language-javascript"><code class=" language-javascript">var x = 23; /*[+ var msg = "This is a working application"; +]*/ var f = function() { ...</code></pre> Will be executed as: <pre class=" language-javascript"><code class=" language-javascript">var x = 23; var msg = "This is a working application"; var f = function() { ...</code></pre> You can include expressions inside these comments, and they will be evaluated: <pre class=" language-javascript"><code class=" language-javascript">var x = 23; /*[+ var msg = "Hello, " + [[${session.user.name}]]; +]*/ var f = function() { ...</code></pre> </section><section id="textual-parser-level-comment-blocks-removing-code" class="level2"> <h2 class="content-index-heading content-index-heading-level-2" name="" id="13.4 Textual parser-level comment blocks: removing code"><em>15.4.</em>13.4 Textual parser-level comment blocks: removing code</h2> In a way similar to that of prototype-only comment blocks, all the three textual template modes (<code>TEXT</code>, <code>JAVASCRIPT</code> and <code>CSS</code>) make it possible to instruct Thymeleaf to remove code between special <code>/*[- */</code> and <code>/* -]*/</code> marks, like this: <pre class=" language-javascript"><code class=" language-javascript">var x = 23; /*[- */ var msg = "This is shown only when executed statically!"; /* -]*/ var f = function() { ...</code></pre> Or this, in <code>TEXT</code> mode: <pre class="prettyprint"><code>... /*[- Note the user is obtained from the session, which must exist -]*/ Welcome [(${session.user.name})]! ...</code></pre> </section><section id="natural-javascript-and-css-templates" class="level2"> <h2 class="content-index-heading content-index-heading-level-2" name="" id="13.5 Natural JavaScript and CSS templates"><em>15.5.</em>13.5 Natural JavaScript and CSS templates</h2> As seen in the previous chapter, JavaScript and CSS inlining offer the possibility to include inlined expressions inside JavaScript/CSS comments, like: <pre class=" language-javascript"><code class=" language-javascript">... var username = /*[[${session.user.name}]]*/ "Sebastian Lychee"; ...</code></pre> …which is valid JavaScript, and once executed could look like: <pre class=" language-html"><code class=" language-html">... var username = "John Apricot"; ...</code></pre> This same <em>trick</em> of enclosing inlined expressions inside comments can in fact be used for the entire textual mode syntax: <pre class="prettyprint"><code> /*[# th:if="${user.admin}"]*/ alert('Welcome admin'); /*[/]*/</code></pre> That alert in the code above will be shown when the template is open statically – because it is 100% valid JavaScript –, and also when the template is run if the user is an admin. It is equivalent to: <pre class="prettyprint"><code> [# th:if="${user.admin}"] alert('Welcome admin'); [/]</code></pre> …which is actually the code to which the initial version is converted during template parsing. Note however that wrapping elements in comments does not clean the lines they live in (to the right until a <code>;</code> is found) as inlined output expressions do. That behaviour is reserved for inlined output expressions only. So Thymeleaf 3.0 allows the development of <strong>complex JavaScript scripts and CSS style sheets in the form of natural templates</strong>, valid both as a <em>prototype</em> and as a <em>working template</em>. </section></section><section id="some-more-pages-for-our-grocery" class="level1"> <h1 class="content-index-heading content-index-heading-level-1" name="" id="14 Some more pages for our grocery"><em>16.</em>14 Some more pages for our grocery</h1> Now we know a lot about using Thymeleaf, we can add some new pages to our website for order management. Note that we will focus on HTML code, but you can have a look at the bundled source code if you want to see the corresponding controllers. <section id="order-list" class="level2"> <h2 class="content-index-heading content-index-heading-level-2" name="" id="14.1 Order List"><em>16.1.</em>14.1 Order List</h2> Let’s start by creating an order list page, <code>/WEB-INF/templates/order/list.html</code>: <pre class=" language-html"><code class=" language-html"><span class="token doctype"><!DOCTYPE html></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name"><span class="token namespace">xmlns:</span>th</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://www.thymeleaf.org<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>Good Thymes Virtual Grocery<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">http-equiv</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Content-Type<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text/html; charset<span class="token punctuation">=</span>UTF-8<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text/css<span class="token punctuation">"</span></span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>all<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>../../../css/gtvg.css<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@{/css/gtvg.css}<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>Order list<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>table</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span><span class="token punctuation">></span></span>DATE<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span><span class="token punctuation">></span></span>CUSTOMER<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span><span class="token punctuation">></span></span>TOTAL<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span> <span class="token attr-name"><span class="token namespace">th:</span>each</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>o : ${orders}<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${oStat.odd}? <span class="token punctuation">'</span>odd<span class="token punctuation">'</span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${#calendars.format(o.date,<span class="token punctuation">'</span>dd/MMM/yyyy<span class="token punctuation">'</span>)}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>13 jan 2011<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${o.customer.name}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Frederic Tomato<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${#aggregates.sum(o.orderLines.{purchasePrice * amount})}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>23.32<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>details.html<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@{/order/details(orderId<span class="token punctuation">=</span>${o.id})}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>view<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>table</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>../home.html<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@{/}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Return to home<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></code></pre> There’s nothing here that should surprise us, except for this little bit of OGNL magic: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${#aggregates.sum(o.orderLines.{purchasePrice * amount})}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>23.32<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span></code></pre> What that does is, for each order line (<code>OrderLine</code> object) in the order, multiply its <code>purchasePrice</code> and <code>amount</code> properties (by calling the corresponding <code>getPurchasePrice()</code> and <code>getAmount()</code> methods) and return the result into a list of numbers, later aggregated by the <code>#aggregates.sum(...)</code> function in order to obtain the order total price. You’ve got to love the power of OGNL. </section><section id="order-details" class="level2"> <h2 class="content-index-heading content-index-heading-level-2" name="" id="14.2 Order Details"><em>16.2.</em>14.2 Order Details</h2> Now for the order details page, in which we will make a heavy use of asterisk syntax: <pre class=" language-html"><code class=" language-html"><span class="token doctype"><!DOCTYPE html></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name"><span class="token namespace">xmlns:</span>th</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://www.thymeleaf.org<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>Good Thymes Virtual Grocery<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">http-equiv</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Content-Type<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text/html; charset<span class="token punctuation">=</span>UTF-8<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text/css<span class="token punctuation">"</span></span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>all<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>../../../css/gtvg.css<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@{/css/gtvg.css}<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span> <span class="token attr-name"><span class="token namespace">th:</span>object</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${order}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>Order details<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>b</span><span class="token punctuation">></span></span>Code:<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>b</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>*{id}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>99<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>b</span><span class="token punctuation">></span></span>Date:<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>b</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>*{#calendars.format(date,<span class="token punctuation">'</span>dd MMM yyyy<span class="token punctuation">'</span>)}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>13 jan 2011<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>Customer<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name"><span class="token namespace">th:</span>object</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>*{customer}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>b</span><span class="token punctuation">></span></span>Name:<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>b</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>*{name}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Frederic Tomato<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>b</span><span class="token punctuation">></span></span>Since:<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>b</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>*{#calendars.format(customerSince,<span class="token punctuation">'</span>dd MMM yyyy<span class="token punctuation">'</span>)}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>1 jan 2011<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>Products<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>table</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span><span class="token punctuation">></span></span>PRODUCT<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span><span class="token punctuation">></span></span>AMOUNT<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span><span class="token punctuation">></span></span>PURCHASE PRICE<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span> <span class="token attr-name"><span class="token namespace">th:</span>each</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>ol,row : *{orderLines}<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${row.odd}? <span class="token punctuation">'</span>odd<span class="token punctuation">'</span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${ol.product.name}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Strawberries<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${ol.amount}<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>number<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>3<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${ol.purchasePrice}<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>number<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>23.32<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>table</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>b</span><span class="token punctuation">></span></span>TOTAL:<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>b</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>*{#aggregates.sum(orderLines.{purchasePrice * amount})}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>35.23<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>list.html<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@{/order/list}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Return to order list<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></code></pre> Not much really new here, except for this nested object selection: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span> <span class="token attr-name"><span class="token namespace">th:</span>object</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${order}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name"><span class="token namespace">th:</span>object</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>*{customer}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>b</span><span class="token punctuation">></span></span>Name:<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>b</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>*{name}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Frederic Tomato<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span></code></pre> …which makes that <code>*{name}</code> equivalent to: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>b</span><span class="token punctuation">></span></span>Name:<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>b</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${order.customer.name}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Frederic Tomato<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></code></pre> </section></section><section id="more-on-configuration" class="level1"> <h1 class="content-index-heading content-index-heading-level-1" name="" id="15 More on Configuration"><em>17.</em>15 More on Configuration</h1> <section id="template-resolvers" class="level2"> <h2 class="content-index-heading content-index-heading-level-2" name="" id="15.1 Template Resolvers"><em>17.1.</em>15.1 Template Resolvers</h2> For our Good Thymes Virtual Grocery, we chose an <code>ITemplateResolver</code> implementation called <code>ServletContextTemplateResolver</code> that allowed us to obtain templates as resources from the Servlet Context. Besides giving us the ability to create our own template resolver by implementing <code>ITemplateResolver,</code> Thymeleaf includes four implementations out of the box: <ul> <li> <code>org.thymeleaf.templateresolver.ClassLoaderTemplateResolver</code>, which resolves templates as classloader resources, like: <pre class=" language-java"><code class=" language-java"><span class="token keyword">return</span> Thread<span class="token punctuation">.</span><span class="token function">currentThread</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getContextClassLoader</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getResourceAsStream</span><span class="token punctuation">(</span>template<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> </li> <li> <code>org.thymeleaf.templateresolver.FileTemplateResolver</code>, which resolves templates as files from the file system, like: <pre class=" language-java"><code class=" language-java"><span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">FileInputStream</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">File</span><span class="token punctuation">(</span>template<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> </li> <li> <code>org.thymeleaf.templateresolver.UrlTemplateResolver</code>, which resolves templates as URLs (even non-local ones), like: <pre class=" language-java"><code class=" language-java"><span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">URL</span><span class="token punctuation">(</span>template<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">openStream</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> </li> <li> <code>org.thymeleaf.templateresolver.StringTemplateResolver</code>, which resolves templates directly as the <code>String</code> being specified as <code>template</code> (or <em>template name</em>, which in this case is obviously much more than a mere name): <pre class=" language-java"><code class=" language-java"><span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">StringReader</span><span class="token punctuation">(</span>templateName<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> </li> </ul> All of the pre-bundled implementations of <code>ITemplateResolver</code> allow the same set of configuration parameters, which include: <ul> <li>Prefix and suffix (as already seen): <pre class=" language-java"><code class=" language-java">templateResolver<span class="token punctuation">.</span><span class="token function">setPrefix</span><span class="token punctuation">(</span><span class="token string">"/WEB-INF/templates/"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> templateResolver<span class="token punctuation">.</span><span class="token function">setSuffix</span><span class="token punctuation">(</span><span class="token string">".html"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> </li> <li>Template aliases that allow the use of template names that do not directly correspond to file names. If both suffix/prefix and alias exist, alias will be applied before prefix/suffix: <pre class=" language-java"><code class=" language-java">templateResolver<span class="token punctuation">.</span><span class="token function">addTemplateAlias</span><span class="token punctuation">(</span><span class="token string">"adminHome"</span><span class="token punctuation">,</span><span class="token string">"profiles/admin/home"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> templateResolver<span class="token punctuation">.</span><span class="token function">setTemplateAliases</span><span class="token punctuation">(</span>aliasesMap<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> </li> <li>Encoding to be applied when reading templates: <pre class=" language-java"><code class=" language-java">templateResolver<span class="token punctuation">.</span><span class="token function">setEncoding</span><span class="token punctuation">(</span><span class="token string">"UTF-8"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> </li> <li>Template mode to be used: <pre class=" language-java"><code class=" language-java"><span class="token comment" spellcheck="true">// Default is HTML</span> templateResolver<span class="token punctuation">.</span><span class="token function">setTemplateMode</span><span class="token punctuation">(</span><span class="token string">"XML"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> </li> <li>Default mode for template cache, and patterns for defining whether specific templates are cacheable or not: <pre class=" language-java"><code class=" language-java"><span class="token comment" spellcheck="true">// Default is true</span> templateResolver<span class="token punctuation">.</span><span class="token function">setCacheable</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> templateResolver<span class="token punctuation">.</span><span class="token function">getCacheablePatternSpec</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addPattern</span><span class="token punctuation">(</span><span class="token string">"/users/*"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> </li> <li>TTL in milliseconds for parsed template cache entries originated in this template resolver. If not set, the only way to remove an entry from the cache will be to exceed the cache max size (oldest entry will be removed). <pre class=" language-java"><code class=" language-java"><span class="token comment" spellcheck="true">// Default is no TTL (only cache size exceeded would remove entries)</span> templateResolver<span class="token punctuation">.</span><span class="token function">setCacheTTLMs</span><span class="token punctuation">(</span>60000L<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> </li> </ul> <blockquote>The Thymeleaf + Spring integration packages offer a <code>SpringResourceTemplateResolver</code> implementation which uses all the Spring infrastructure for accessing and reading resources in applications, and which is the recommended implementation in Spring-enabled applications.</blockquote> <section id="chaining-template-resolvers" class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="Chaining Template Resolvers"><em>17.1.1.</em>Chaining Template Resolvers</h3> Also, a Template Engine can specify several template resolvers, in which case an order can be established between them for template resolution so that, if the first one is not able to resolve the template, the second one is asked, and so on: <pre class=" language-java"><code class=" language-java">ClassLoaderTemplateResolver classLoaderTemplateResolver <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ClassLoaderTemplateResolver</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> classLoaderTemplateResolver<span class="token punctuation">.</span><span class="token function">setOrder</span><span class="token punctuation">(</span>Integer<span class="token punctuation">.</span><span class="token function">valueOf</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> ServletContextTemplateResolver servletContextTemplateResolver <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ServletContextTemplateResolver</span><span class="token punctuation">(</span>servletContext<span class="token punctuation">)</span><span class="token punctuation">;</span> servletContextTemplateResolver<span class="token punctuation">.</span><span class="token function">setOrder</span><span class="token punctuation">(</span>Integer<span class="token punctuation">.</span><span class="token function">valueOf</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> templateEngine<span class="token punctuation">.</span><span class="token function">addTemplateResolver</span><span class="token punctuation">(</span>classLoaderTemplateResolver<span class="token punctuation">)</span><span class="token punctuation">;</span> templateEngine<span class="token punctuation">.</span><span class="token function">addTemplateResolver</span><span class="token punctuation">(</span>servletContextTemplateResolver<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> When several template resolvers are applied, it is recommended to specify patterns for each template resolver so that Thymeleaf can quickly discard those template resolvers that are not meant to resolve the template, enhancing performance. Doing this is not a requirement, but a recommendation: <pre class=" language-java"><code class=" language-java">ClassLoaderTemplateResolver classLoaderTemplateResolver <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ClassLoaderTemplateResolver</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> classLoaderTemplateResolver<span class="token punctuation">.</span><span class="token function">setOrder</span><span class="token punctuation">(</span>Integer<span class="token punctuation">.</span><span class="token function">valueOf</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">// This classloader will not be even asked for any templates not matching these patterns </span> classLoaderTemplateResolver<span class="token punctuation">.</span><span class="token function">getResolvablePatternSpec</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addPattern</span><span class="token punctuation">(</span><span class="token string">"/layout/*.html"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> classLoaderTemplateResolver<span class="token punctuation">.</span><span class="token function">getResolvablePatternSpec</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addPattern</span><span class="token punctuation">(</span><span class="token string">"/menu/*.html"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> ServletContextTemplateResolver servletContextTemplateResolver <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ServletContextTemplateResolver</span><span class="token punctuation">(</span>servletContext<span class="token punctuation">)</span><span class="token punctuation">;</span> servletContextTemplateResolver<span class="token punctuation">.</span><span class="token function">setOrder</span><span class="token punctuation">(</span>Integer<span class="token punctuation">.</span><span class="token function">valueOf</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> If these <em>resolvable patterns</em> are not specified, we will be relying on the specific capabilities of each of the <code>ITemplateResolver</code> implementations we are using. Note that not all implementations might be able to determine the existence of a template before resolving, and thus could always consider a template as <em>resolvable</em> and break the resolution chain (not allowing other resolvers to check for the same template), but then be unable to read the real resource. All the <code>ITemplateResolver</code> implementations that are included with core Thymeleaf include a mechanism that will allow us to make the resolvers <em>really check</em> if a resource exists before considering it <em>resolvable</em>. It is the <code>checkExistence</code> flag, which works like: <pre class=" language-java"><code class=" language-java">ClassLoaderTemplateResolver classLoaderTemplateResolver <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ClassLoaderTemplateResolver</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> classLoaderTemplateResolver<span class="token punctuation">.</span><span class="token function">setOrder</span><span class="token punctuation">(</span>Integer<span class="token punctuation">.</span><span class="token function">valueOf</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> classLoaderTempalteResolver<span class="token punctuation">.</span><span class="token function">setCheckExistence</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> This <code>checkExistence</code> flag forces the resolver perform a <em>real check</em> for resource existence during the resolution phase (and let the following resolver in the chain be called if existence check returns false). While this might sound good in every case, in most cases this will mean a double access to the resource itself (once for checking existence, another time for reading it), and could be a performance issue in some scenarios, e.g. remote URL-based template resources – a potential performance issue that might anyway get largely mitigated by the use of the template cache (in which case templates will only be <em>resolved</em> the first time they are accessed). </section></section><section id="message-resolvers" class="level2"> <h2 class="content-index-heading content-index-heading-level-2" name="" id="15.2 Message Resolvers"><em>17.2.</em>15.2 Message Resolvers</h2> We did not explicitly specify a Message Resolver implementation for our Grocery application, and as it was explained before, this meant that the implementation being used was an <code>org.thymeleaf.messageresolver.StandardMessageResolver</code> object. <code>StandardMessageResolver</code> is the standard implementation of the <code>IMessageResolver</code> interface, but we could create our own if we wanted, adapted to the specific needs of our application. <blockquote>The Thymeleaf + Spring integration packages offer by default an <code>IMessageResolver</code> implementation which uses the standard Spring way of retrieving externalized messages, by using <code>MessageSource</code> beans declared at the Spring Application Context.</blockquote> <section id="standard-message-resolver" class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="Standard Message Resolver"><em>17.2.1.</em>Standard Message Resolver</h3> So how does <code>StandardMessageResolver</code> look for the messages requested at a specific template? If the template name is <code>home</code> and it is located in <code>/WEB-INF/templates/home.html</code>, and the requested locale is <code>gl_ES</code> then this resolver will look for messages in the following files, in this order: <ul> <li><code>/WEB-INF/templates/home_gl_ES.properties</code></li> <li><code>/WEB-INF/templates/home_gl.properties</code></li> <li><code>/WEB-INF/templates/home.properties</code></li> </ul> Refer to the JavaDoc documentation of the <code>StandardMessageResolver</code> class for more detail on how the complete message resolution mechanism works. </section><section id="configuring-message-resolvers" class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="Configuring message resolvers"><em>17.2.2.</em>Configuring message resolvers</h3> What if we wanted to add a message resolver (or more) to the Template Engine? Easy: <pre class=" language-java"><code class=" language-java"><span class="token comment" spellcheck="true">// For setting only one</span> templateEngine<span class="token punctuation">.</span><span class="token function">setMessageResolver</span><span class="token punctuation">(</span>messageResolver<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">// For setting more than one</span> templateEngine<span class="token punctuation">.</span><span class="token function">addMessageResolver</span><span class="token punctuation">(</span>messageResolver<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> And why would we want to have more than one message resolver? For the same reason as template resolvers: message resolvers are ordered and if the first one cannot resolve a specific message, the second one will be asked, then the third, etc. </section></section><section id="conversion-services" class="level2"> <h2 class="content-index-heading content-index-heading-level-2" name="" id="15.3 Conversion Services"><em>17.3.</em>15.3 Conversion Services</h2> The <em>conversion service</em> that enables us to perform data conversion and formatting operations by means of the <em>double-brace</em> syntax (<code>${{...}}</code>) is actually a feature of the Standard Dialect, not of the Thymeleaf Template Engine itself. As such, the way to configure it is by setting our custom implementation of the <code>IStandardConversionService</code> interface directly into the instance of <code>StandardDialect</code> that is being configured into the template engine. Like: <pre class=" language-java"><code class=" language-java">IStandardConversionService customConversionService <span class="token operator">=</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> StandardDialect dialect <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">StandardDialect</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> dialect<span class="token punctuation">.</span><span class="token function">setConversionService</span><span class="token punctuation">(</span>customConversionService<span class="token punctuation">)</span><span class="token punctuation">;</span> templateEngine<span class="token punctuation">.</span><span class="token function">setDialect</span><span class="token punctuation">(</span>dialect<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <blockquote>Note that the thymeleaf-spring3 and thymeleaf-spring4 packages contain the <code>SpringStandardDialect</code>, and this dialect already comes pre-configured with an implementation of <code>IStandardConversionService</code> that integrates Spring’s own <em>Conversion Service</em> infrastructure into Thymeleaf.</blockquote> </section><section id="logging" class="level2"> <h2 class="content-index-heading content-index-heading-level-2" name="" id="15.4 Logging"><em>17.4.</em>15.4 Logging</h2> Thymeleaf pays quite a lot of attention to logging, and always tries to offer the maximum amount of useful information through its logging interface. The logging library used is <code>slf4j,</code> which in fact acts as a bridge to whichever logging implementation we might want to use in our application (for example, <code>log4j</code>). Thymeleaf classes will log <code>TRACE</code>, <code>DEBUG</code> and <code>INFO</code>-level information, depending on the level of detail we desire, and besides general logging it will use three special loggers associated with the TemplateEngine class which we can configure separately for different purposes: <ul> <li> <code>org.thymeleaf.TemplateEngine.CONFIG</code> will output detailed configuration of the library during initialization.</li> <li> <code>org.thymeleaf.TemplateEngine.TIMER</code> will output information about the amount of time taken to process each template (useful for benchmarking!)</li> <li> <code>org.thymeleaf.TemplateEngine.cache</code> is the prefix for a set of loggers that output specific information about the caches. Although the names of the cache loggers are configurable by the user and thus could change, by default they are: <ul> <li><code>org.thymeleaf.TemplateEngine.cache.TEMPLATE_CACHE</code></li> <li><code>org.thymeleaf.TemplateEngine.cache.EXPRESSION_CACHE</code></li> </ul> </li> </ul> An example configuration for Thymeleaf’s logging infrastructure, using <code>log4j</code>, could be: <pre class="prettyprint"><code>log4j.logger.org.thymeleaf=DEBUG log4j.logger.org.thymeleaf.TemplateEngine.CONFIG=TRACE log4j.logger.org.thymeleaf.TemplateEngine.TIMER=TRACE log4j.logger.org.thymeleaf.TemplateEngine.cache.TEMPLATE_CACHE=TRACE</code></pre> </section></section><section id="template-cache" class="level1"> <h1 class="content-index-heading content-index-heading-level-1" name="" id="16 Template Cache"><em>18.</em>16 Template Cache</h1> Thymeleaf works thanks to a set of parsers – for markup and text – that parse templates into sequences of events (open tag, text, close tag, comment, etc.) and a series of processors – one for each type of behaviour that needs to be applied – that modify the template parsed event sequence in order to create the results we expect by combining the original template with our data. It also includes – by default – a cache that stores parsed templates; the sequence of events resulting from reading and parsing template files before processing them. This is especially useful when working in a web application, and builds on the following concepts: <ul> <li>Input/Output is almost always the slowest part of any application. In-memory processing is extremely quick by comparison.</li> <li>Cloning an existing in-memory event sequence is always much quicker than reading a template file, parsing it and creating a new event sequence for it.</li> <li>Web applications usually have only a few dozen templates.</li> <li>Template files are small-to-medium size, and they are not modified while the application is running.</li> </ul> This all leads to the idea that caching the most used templates in a web application is feasible without wasting large amounts of memory, and also that it will save a lot of time that would be spent on input/output operations on a small set of files that, in fact, never change. And how can we take control of this cache? First, we’ve learned before that we can enable or disable it at the Template Resolver, even acting only on specific templates: <pre class=" language-java"><code class=" language-java"><span class="token comment" spellcheck="true">// Default is true</span> templateResolver<span class="token punctuation">.</span><span class="token function">setCacheable</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> templateResolver<span class="token punctuation">.</span><span class="token function">getCacheablePatternSpec</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addPattern</span><span class="token punctuation">(</span><span class="token string">"/users/*"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> Also, we could modify its configuration by establishing our own <em>Cache Manager</em> object, which could be an instance of the default <code>StandardCacheManager</code> implementation: <pre class=" language-java"><code class=" language-java"><span class="token comment" spellcheck="true">// Default is 200</span> StandardCacheManager cacheManager <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">StandardCacheManager</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> cacheManager<span class="token punctuation">.</span><span class="token function">setTemplateCacheMaxSize</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> templateEngine<span class="token punctuation">.</span><span class="token function">setCacheManager</span><span class="token punctuation">(</span>cacheManager<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> Refer to the javadoc API of <code>org.thymeleaf.cache.StandardCacheManager</code> for more info on configuring the caches. Entries can be manually removed from the template cache: <pre class=" language-java"><code class=" language-java"><span class="token comment" spellcheck="true">// Clear the cache completely</span> templateEngine<span class="token punctuation">.</span><span class="token function">clearTemplateCache</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">// Clear a specific template from the cache</span> templateEngine<span class="token punctuation">.</span><span class="token function">clearTemplateCacheFor</span><span class="token punctuation">(</span><span class="token string">"/users/userList"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> </section><section id="decoupled-template-logic" class="level1"> <h1 class="content-index-heading content-index-heading-level-1" name="" id="17 Decoupled Template Logic"><em>19.</em>17 Decoupled Template Logic</h1> <section id="decoupled-logic-the-concept" class="level2"> <h2 class="content-index-heading content-index-heading-level-2" name="" id="17.1 Decoupled logic: The concept"><em>19.1.</em>17.1 Decoupled logic: The concept</h2> So far we have worked for our Grocery Store with templates done the <em>usual way</em>, with logic being inserted into our templates in the form of attributes. But Thymeleaf also allows us to completely <em>decouple</em> the template markup from its logic, allowing the creation of <strong>completely logic-less markup templates</strong> in the <code>HTML</code> and <code>XML</code> template modes. The main idea is that template logic will be defined in a separate <em>logic file</em> (more exactly a <em>logic resource</em>, as it doesn’t need to be a <em>file</em>). By default, that logic resource will be an additional file living in the same place ( e.g. folder) as the template file, with the same name but with <code>.th.xml</code> extension: <pre class="prettyprint"><code>/templates +->/home.html +->/home.th.xml</code></pre> So the <code>home.html</code> file can be completely logic-less. It might look like this: <pre class=" language-html"><code class=" language-html"><span class="token doctype"><!DOCTYPE html></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>table</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>usersTable<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>username<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Jeremy Grapefruit<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>usertype<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Normal User<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>username<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Alice Watermelon<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>usertype<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Administrator<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>table</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></code></pre> Absolutely no Thymeleaf code there. This is a template file that a designer with no Thymeleaf or templating knowledge could have created, edited and/or understood. Or a fragment of HTML provided by some external system with no Thymeleaf hooks at all. Let’s now turn that <code>home.html</code> template into a Thymeleaf template by creating our additional <code>home.th.xml</code> file like this: <pre class=" language-xml"><code class=" language-xml"><span class="token prolog"><?xml version="1.0"?></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>thlogic</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>attr</span> <span class="token attr-name">sel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>#usersTable<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>remove</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>all-but-first<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>attr</span> <span class="token attr-name">sel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/tr[0]<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>each</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>user : ${users}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>attr</span> <span class="token attr-name">sel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>td.username<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${user.name}<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>attr</span> <span class="token attr-name">sel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>td.usertype<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>#{|user.type.${user.type}|}<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>attr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>attr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>thlogic</span><span class="token punctuation">></span></span></code></pre> Here we can see a lot of <code><attr></code> tags inside a <code>thlogic</code> block. Those <code><attr></code> tags perform <em>attribute injection</em> on nodes of the original template selected by means of their <code>sel</code> attributes, which contain Thymeleaf <em>markup selectors</em> (actually <em>AttoParser markup selectors</em>). Also note that <code><attr></code> tags can be nested so that their selectors are <em>appended</em>. That <code>sel="/tr[0]"</code> above, for example, will be processed as <code>sel="#usersTable/tr[0]"</code>. And the selector for the user name <code><td></code> will be processed as <code>sel="#usersTable/tr[0]//td.username"</code>. So once merged, both files seen above will be the same as: <pre class=" language-html"><code class=" language-html"><span class="token doctype"><!DOCTYPE html></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>table</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>usersTable<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>remove</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>all-but-first<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span> <span class="token attr-name"><span class="token namespace">th:</span>each</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>user : ${users}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>username<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>${user.name}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Jeremy Grapefruit<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>usertype<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">th:</span>text</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>#{|user.type.${user.type}|}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Normal User<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>username<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Alice Watermelon<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>usertype<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Administrator<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>table</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></code></pre> This looks more familiar, and is indeed less <em>verbose</em> than creating two separate files. But the advantage of <em>decoupled templates</em> is that we can give for our templates total independence from Thymeleaf, and therefore better maintainability from the design standpoint. Of course some <em>contracts</em> between designers or developers will still be needed – e.g. the fact that the users <code><table></code> will need an <code>id="usersTable"</code> –, but in many scenarios a pure-HTML template will be a much better communication artifact between design and development teams. </section><section id="configuring-decoupled-templates" class="level2"> <h2 class="content-index-heading content-index-heading-level-2" name="" id="17.2 Configuring decoupled templates"><em>19.2.</em>17.2 Configuring decoupled templates</h2> <section id="enabling-decoupled-templates" class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="Enabling decoupled templates"><em>19.2.1.</em>Enabling decoupled templates</h3> Decoupled logic will not be expected for every template by default. Instead, the configured template resolvers (implementations of <code>ITemplateResolver</code>) will need to specifically mark the templates they resolve as <em>using decoupled logic</em>. Except for <code>StringTemplateResolver</code> (which does not allow decoupled logic), all other out-of-the-box implementations of <code>ITemplateResolver</code> will provide a flag called <code>useDecoupledLogic</code> that will mark all templates resolved by that resolver as potentially having all or part of its logic living in a separate resource: <pre class=" language-java"><code class=" language-java"><span class="token keyword">final</span> ServletContextTemplateResolver templateResolver <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ServletContextTemplateResolver</span><span class="token punctuation">(</span>servletContext<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> templateResolver<span class="token punctuation">.</span><span class="token function">setUseDecoupledLogic</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> </section><section id="mixing-coupled-and-decoupled-logic" class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="Mixing coupled and decoupled logic"><em>19.2.2.</em>Mixing coupled and decoupled logic</h3> Decoupled template logic, when enabled, is not a requirement. When enabled, it means that the engine will <em>look for</em> a resource containing decoupled logic, parsing and merging it with the original template if it exists. No error will be thrown if the decoupled logic resource does not exist. Also, in the same template we can mix both <em>coupled</em> and <em>decoupled</em> logic, for example by adding some Thymeleaf attributes at the original template file but leaving others for the separate decoupled logic file. The most common case for this is using the new (in v3.0) <code>th:ref</code> attribute. </section></section><section id="the-thref-attribute" class="level2"> <h2 class="content-index-heading content-index-heading-level-2" name="" id="17.3 The th:ref attribute"><em>19.3.</em>17.3 The th:ref attribute</h2> <code>th:ref</code> is only a marker attribute. It does nothing from the processing standpoint and simply disappears when the template is processed, but its usefulness lies in the fact that it acts as a <em>markup reference</em>, i.e. it can be resolved by name from a <em>markup selector</em> just like a <em>tag name</em> or a <em>fragment</em> (<code>th:fragment</code>). So if we have a selector like: <pre class=" language-xml"><code class=" language-xml"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>attr</span> <span class="token attr-name">sel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>whatever<span class="token punctuation">"</span></span> <span class="token attr-name">...</span><span class="token punctuation">/></span></span></code></pre> This will match: <ul> <li>Any <code><whatever></code> tags.</li> <li>Any tags with a <code>th:fragment="whatever"</code> attribute.</li> <li>Any tags with a <code>th:ref="whatever"</code> attribute.</li> </ul> What is the advantage of <code>th:ref</code> against, for example, using a pure-HTML <code>id</code> attribute? Merely the fact that we might not want to add so many <code>id</code> and <code>class</code> attributes to our tags to act as <em>logic anchors</em>, which might end up <em>polluting</em> our output. And in the same sense, what is the disadvantage of <code>th:ref</code>? Well, obviously that we’d be adding a bit of Thymeleaf logic (<em>“logic”</em>) to our templates. Note this applicability of the <code>th:ref</code> attribute <strong>does not only apply to decoupled logic template files</strong>: it works the same in other types of scenarios, like in fragment expressions (<code>~{...}</code>). </section><section id="performance-impact-of-decoupled-templates" class="level2"> <h2 class="content-index-heading content-index-heading-level-2" name="" id="17.4 Performance impact of decoupled templates"><em>19.4.</em>17.4 Performance impact of decoupled templates</h2> The impact is extremely small. When a resolved template is marked to use decoupled logic and it is not cached, the template logic resource will be resolved first, parsed and processed into a secuence of instructions in-memory: basically a list of attributes to be injected to each markup selector. But this is the only <em>additional step</em> required because, after this, the real template will be parsed, and while it is parsed these attributes will be injected <em>on-the-fly</em> by the parser itself, thanks to the advanced capabilities for node selection in AttoParser. So parsed nodes will come out of the parser as if they had their injected attributes written in the original template file. The biggest advantage of this? When a template is configured to be cached, it will be cached already containing the injected attributes. So the overhead of using <em>decoupled templates</em> for cacheable templates, once they are cached, will be absolutely <em>zero</em>. </section><section id="resolution-of-decoupled-logic" class="level2"> <h2 class="content-index-heading content-index-heading-level-2" name="" id="17.5 Resolution of decoupled logic"><em>19.5.</em>17.5 Resolution of decoupled logic</h2> The way Thymeleaf resolves the decoupled logic resources corresponding to each template is configurable by the user. It is determined by an extension point, the <code>org.thymeleaf.templateparser.markup.decoupled.IDecoupledTemplateLogicResolver</code>, for which a <em>default implementation</em> is provided: <code>StandardDecoupledTemplateLogicResolver</code>. What does this standard implementation do? <ul> <li>First, it applies a <code>prefix</code> and a <code>suffix</code> to the <em>base name</em> of the template resource (obtained by means of its <code>ITemplateResource#getBaseName()</code> method). Both prefix and suffix can be configured and, by default, the prefix will be empty and the suffix will be <code>.th.xml</code>.</li> <li>Second, it asks the template resource to resolve a <em>relative resource</em> with the computed name by means of its <code>ITemplateResource#relative(String relativeLocation)</code> method.</li> </ul> The specific implementation of <code>IDecoupledTemplateLogicResolver</code> to be used can be configured at the <code>TemplateEngine</code> easily: <pre class=" language-java"><code class=" language-java"><span class="token keyword">final</span> StandardDecoupledTemplateLogicResolver decoupledresolver <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">StandardDecoupledTemplateLogicResolver</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> decoupledResolver<span class="token punctuation">.</span><span class="token function">setPrefix</span><span class="token punctuation">(</span><span class="token string">"../viewlogic/"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> templateEngine<span class="token punctuation">.</span><span class="token function">setDecoupledTemplateLogicResolver</span><span class="token punctuation">(</span>decoupledResolver<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> </section></section><section id="appendix-a-expression-basic-objects" class="level1"> <h1 class="content-index-heading content-index-heading-level-1" name="" id="18 Appendix A: Expression Basic Objects"><em>20.</em>18 Appendix A: Expression Basic Objects</h1> Some objects and variable maps are always available to be invoked. Let’s see them: <section id="base-objects" class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="Base objects"><em>20.1.1.</em>Base objects</h3> <ul> <li> <strong>#ctx</strong> : the context object. An implementation of <code>org.thymeleaf.context.IContext</code> or <code>org.thymeleaf.context.IWebContext</code> depending on our environment (standalone or web). Note <code>#vars</code> and <code>#root</code> are synomyns for the same object, but using <code>#ctx</code> is recommended.</li> </ul> <pre class=" language-java"><code class=" language-java"><span class="token comment" spellcheck="true">/* * ====================================================================== * See javadoc API for class org.thymeleaf.context.IContext * ====================================================================== */</span> $<span class="token punctuation">{</span>#ctx<span class="token punctuation">.</span>locale<span class="token punctuation">}</span> $<span class="token punctuation">{</span>#ctx<span class="token punctuation">.</span>variableNames<span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * ====================================================================== * See javadoc API for class org.thymeleaf.context.IWebContext * ====================================================================== */</span> $<span class="token punctuation">{</span>#ctx<span class="token punctuation">.</span>request<span class="token punctuation">}</span> $<span class="token punctuation">{</span>#ctx<span class="token punctuation">.</span>response<span class="token punctuation">}</span> $<span class="token punctuation">{</span>#ctx<span class="token punctuation">.</span>session<span class="token punctuation">}</span> $<span class="token punctuation">{</span>#ctx<span class="token punctuation">.</span>servletContext<span class="token punctuation">}</span></code></pre> <ul> <li> <strong>#locale</strong> : direct access to the <code>java.util.Locale</code> associated with current request.</li> </ul> <pre class=" language-java"><code class=" language-java">$<span class="token punctuation">{</span>#locale<span class="token punctuation">}</span></code></pre> </section><section id="web-context-namespaces-for-requestsession-attributes-etc." class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="Web context namespaces for request/session attributes, etc."><em>20.1.2.</em>Web context namespaces for request/session attributes, etc.</h3> When using Thymeleaf in a web environment, we can use a series of shortcuts for accessing request parameters, session attributes and application attributes: <blockquote>Note these are not <em>context objects</em>, but maps added to the context as variables, so we access them without <code>#</code>. In some way, they act as <em>namespaces</em>.</blockquote> <ul> <li> <strong>param</strong> : for retrieving request parameters. <code>${param.foo}</code> is a <code>String[]</code> with the values of the <code>foo</code> request parameter, so <code>${param.foo[0]}</code> will normally be used for getting the first value.</li> </ul> <pre class=" language-java"><code class=" language-java"><span class="token comment" spellcheck="true">/* * ============================================================================ * See javadoc API for class org.thymeleaf.context.WebRequestParamsVariablesMap * ============================================================================ */</span> $<span class="token punctuation">{</span>param<span class="token punctuation">.</span>foo<span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// Retrieves a String[] with the values of request parameter 'foo'</span> $<span class="token punctuation">{</span>param<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>param<span class="token punctuation">.</span><span class="token function">isEmpty</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>param<span class="token punctuation">.</span><span class="token function">containsKey</span><span class="token punctuation">(</span><span class="token string">'foo'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span></code></pre> <ul> <li> <strong>session</strong> : for retrieving session attributes.</li> </ul> <pre class=" language-java"><code class=" language-java"><span class="token comment" spellcheck="true">/* * ====================================================================== * See javadoc API for class org.thymeleaf.context.WebSessionVariablesMap * ====================================================================== */</span> $<span class="token punctuation">{</span>session<span class="token punctuation">.</span>foo<span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// Retrieves the session atttribute 'foo'</span> $<span class="token punctuation">{</span>session<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>session<span class="token punctuation">.</span><span class="token function">isEmpty</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>session<span class="token punctuation">.</span><span class="token function">containsKey</span><span class="token punctuation">(</span><span class="token string">'foo'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span></code></pre> <ul> <li> <strong>application</strong> : for retrieving application/servlet context attributes.</li> </ul> <pre class=" language-java"><code class=" language-java"><span class="token comment" spellcheck="true">/* * ============================================================================= * See javadoc API for class org.thymeleaf.context.WebServletContextVariablesMap * ============================================================================= */</span> $<span class="token punctuation">{</span>application<span class="token punctuation">.</span>foo<span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// Retrieves the ServletContext atttribute 'foo'</span> $<span class="token punctuation">{</span>application<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>application<span class="token punctuation">.</span><span class="token function">isEmpty</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>application<span class="token punctuation">.</span><span class="token function">containsKey</span><span class="token punctuation">(</span><span class="token string">'foo'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span></code></pre> Note there is <strong>no need to specify a namespace for accessing request attributes</strong> (as opposed to <em>request parameters</em>) because all request attributes are automatically added to the context as variables in the context root: <pre class=" language-java"><code class=" language-java">$<span class="token punctuation">{</span>myRequestAttribute<span class="token punctuation">}</span></code></pre> </section><section id="web-context-objects" class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="Web context objects"><em>20.1.3.</em>Web context objects</h3> Inside a web environment there is also direct access to the following objects (note these are objects, not maps/namespaces): <ul> <li> <strong>#request</strong> : direct access to the <code>javax.servlet.http.HttpServletRequest</code> object associated with the current request.</li> </ul> <pre class=" language-java"><code class=" language-java">$<span class="token punctuation">{</span>#request<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">'foo'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#request<span class="token punctuation">.</span><span class="token function">getParameter</span><span class="token punctuation">(</span><span class="token string">'foo'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#request<span class="token punctuation">.</span><span class="token function">getContextPath</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#request<span class="token punctuation">.</span><span class="token function">getRequestName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span></code></pre> <ul> <li> <strong>#session</strong> : direct access to the <code>javax.servlet.http.HttpSession</code> object associated with the current request.</li> </ul> <pre class=" language-java"><code class=" language-java">$<span class="token punctuation">{</span>#session<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">'foo'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#session<span class="token punctuation">.</span>id<span class="token punctuation">}</span> $<span class="token punctuation">{</span>#session<span class="token punctuation">.</span>lastAccessedTime<span class="token punctuation">}</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span></code></pre> <ul> <li> <strong>#servletContext</strong> : direct access to the <code>javax.servlet.ServletContext</code> object associated with the current request.</li> </ul> <pre class=" language-java"><code class=" language-java">$<span class="token punctuation">{</span>#servletContext<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">'foo'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#servletContext<span class="token punctuation">.</span>contextPath<span class="token punctuation">}</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span></code></pre> </section></section><section id="appendix-b-expression-utility-objects" class="level1"> <h1 class="content-index-heading content-index-heading-level-1" name="" id="19 Appendix B: Expression Utility Objects"><em>21.</em>19 Appendix B: Expression Utility Objects</h1> <section id="execution-info" class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="Execution Info"><em>21.1.1.</em>Execution Info</h3> <ul> <li> <strong>#execInfo</strong> : expression object providing useful information about the template being processed inside Thymeleaf Standard Expressions.</li> </ul> <pre class=" language-java"><code class=" language-java"><span class="token comment" spellcheck="true">/* * ====================================================================== * See javadoc API for class org.thymeleaf.expression.ExecutionInfo * ====================================================================== */</span> <span class="token comment" spellcheck="true">/* * Return the name and mode of the 'leaf' template. This means the template * from where the events being processed were parsed. So if this piece of * code is not in the root template "A" but on a fragment being inserted * into "A" from another template called "B", this will return "B" as a * name, and B's mode as template mode. */</span> $<span class="token punctuation">{</span>#execInfo<span class="token punctuation">.</span>templateName<span class="token punctuation">}</span> $<span class="token punctuation">{</span>#execInfo<span class="token punctuation">.</span>templateMode<span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Return the name and mode of the 'root' template. This means the template * that the template engine was originally asked to process. So if this * piece of code is not in the root template "A" but on a fragment being * inserted into "A" from another template called "B", this will still * return "A" and A's template mode. */</span> $<span class="token punctuation">{</span>#execInfo<span class="token punctuation">.</span>processedTemplateName<span class="token punctuation">}</span> $<span class="token punctuation">{</span>#execInfo<span class="token punctuation">.</span>processedTemplateMode<span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Return the stacks (actually, List<String> or List<TemplateMode>) of * templates being processed. The first element will be the * 'processedTemplate' (the root one), the last one will be the 'leaf' * template, and in the middle all the fragments inserted in nested * manner to reach the leaf from the root will appear. */</span> $<span class="token punctuation">{</span>#execInfo<span class="token punctuation">.</span>templateNames<span class="token punctuation">}</span> $<span class="token punctuation">{</span>#execInfo<span class="token punctuation">.</span>templateModes<span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Return the stack of templates being processed similarly (and in the * same order) to 'templateNames' and 'templateModes', but returning * a List<TemplateData> with the full template metadata. */</span> $<span class="token punctuation">{</span>#execInfo<span class="token punctuation">.</span>templateStack<span class="token punctuation">}</span></code></pre> </section><section id="messages-1" class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="Messages"><em>21.1.2.</em>Messages</h3> <ul> <li> <strong>#messages</strong> : utility methods for obtaining externalized messages inside variables expressions, in the same way as they would be obtained using <code>#{...}</code> syntax.</li> </ul> <pre class=" language-java"><code class=" language-java"><span class="token comment" spellcheck="true">/* * ====================================================================== * See javadoc API for class org.thymeleaf.expression.Messages * ====================================================================== */</span> <span class="token comment" spellcheck="true">/* * Obtain externalized messages. Can receive a single key, a key plus arguments, * or an array/list/set of keys (in which case it will return an array/list/set of * externalized messages). * If a message is not found, a default message (like '??msgKey??') is returned. */</span> $<span class="token punctuation">{</span>#messages<span class="token punctuation">.</span><span class="token function">msg</span><span class="token punctuation">(</span><span class="token string">'msgKey'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#messages<span class="token punctuation">.</span><span class="token function">msg</span><span class="token punctuation">(</span><span class="token string">'msgKey'</span><span class="token punctuation">,</span> param1<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#messages<span class="token punctuation">.</span><span class="token function">msg</span><span class="token punctuation">(</span><span class="token string">'msgKey'</span><span class="token punctuation">,</span> param1<span class="token punctuation">,</span> param2<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#messages<span class="token punctuation">.</span><span class="token function">msg</span><span class="token punctuation">(</span><span class="token string">'msgKey'</span><span class="token punctuation">,</span> param1<span class="token punctuation">,</span> param2<span class="token punctuation">,</span> param3<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#messages<span class="token punctuation">.</span><span class="token function">msgWithParams</span><span class="token punctuation">(</span><span class="token string">'msgKey'</span><span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">Object</span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">{</span>param1<span class="token punctuation">,</span> param2<span class="token punctuation">,</span> param3<span class="token punctuation">,</span> param4<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#messages<span class="token punctuation">.</span><span class="token function">arrayMsg</span><span class="token punctuation">(</span>messageKeyArray<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#messages<span class="token punctuation">.</span><span class="token function">listMsg</span><span class="token punctuation">(</span>messageKeyList<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#messages<span class="token punctuation">.</span><span class="token function">setMsg</span><span class="token punctuation">(</span>messageKeySet<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Obtain externalized messages or null. Null is returned instead of a default * message if a message for the specified key is not found. */</span> $<span class="token punctuation">{</span>#messages<span class="token punctuation">.</span><span class="token function">msgOrNull</span><span class="token punctuation">(</span><span class="token string">'msgKey'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#messages<span class="token punctuation">.</span><span class="token function">msgOrNull</span><span class="token punctuation">(</span><span class="token string">'msgKey'</span><span class="token punctuation">,</span> param1<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#messages<span class="token punctuation">.</span><span class="token function">msgOrNull</span><span class="token punctuation">(</span><span class="token string">'msgKey'</span><span class="token punctuation">,</span> param1<span class="token punctuation">,</span> param2<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#messages<span class="token punctuation">.</span><span class="token function">msgOrNull</span><span class="token punctuation">(</span><span class="token string">'msgKey'</span><span class="token punctuation">,</span> param1<span class="token punctuation">,</span> param2<span class="token punctuation">,</span> param3<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#messages<span class="token punctuation">.</span><span class="token function">msgOrNullWithParams</span><span class="token punctuation">(</span><span class="token string">'msgKey'</span><span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">Object</span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">{</span>param1<span class="token punctuation">,</span> param2<span class="token punctuation">,</span> param3<span class="token punctuation">,</span> param4<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#messages<span class="token punctuation">.</span><span class="token function">arrayMsgOrNull</span><span class="token punctuation">(</span>messageKeyArray<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#messages<span class="token punctuation">.</span><span class="token function">listMsgOrNull</span><span class="token punctuation">(</span>messageKeyList<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#messages<span class="token punctuation">.</span><span class="token function">setMsgOrNull</span><span class="token punctuation">(</span>messageKeySet<span class="token punctuation">)</span><span class="token punctuation">}</span></code></pre> </section><section id="urisurls" class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="URIs/URLs"><em>21.1.3.</em>URIs/URLs</h3> <ul> <li> <strong>#uris</strong> : utility object for performing URI/URL operations ( esp. escaping/unescaping) inside Thymeleaf Standard Expressions.</li> </ul> <pre class=" language-java"><code class=" language-java"><span class="token comment" spellcheck="true">/* * ====================================================================== * See javadoc API for class org.thymeleaf.expression.Uris * ====================================================================== */</span> <span class="token comment" spellcheck="true">/* * Escape/Unescape as a URI/URL path */</span> $<span class="token punctuation">{</span>#uris<span class="token punctuation">.</span><span class="token function">escapePath</span><span class="token punctuation">(</span>uri<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#uris<span class="token punctuation">.</span><span class="token function">escapePath</span><span class="token punctuation">(</span>uri<span class="token punctuation">,</span> encoding<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#uris<span class="token punctuation">.</span><span class="token function">unescapePath</span><span class="token punctuation">(</span>uri<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#uris<span class="token punctuation">.</span><span class="token function">unescapePath</span><span class="token punctuation">(</span>uri<span class="token punctuation">,</span> encoding<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Escape/Unescape as a URI/URL path segment (between '/' symbols) */</span> $<span class="token punctuation">{</span>#uris<span class="token punctuation">.</span><span class="token function">escapePathSegment</span><span class="token punctuation">(</span>uri<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#uris<span class="token punctuation">.</span><span class="token function">escapePathSegment</span><span class="token punctuation">(</span>uri<span class="token punctuation">,</span> encoding<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#uris<span class="token punctuation">.</span><span class="token function">unescapePathSegment</span><span class="token punctuation">(</span>uri<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#uris<span class="token punctuation">.</span><span class="token function">unescapePathSegment</span><span class="token punctuation">(</span>uri<span class="token punctuation">,</span> encoding<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Escape/Unescape as a Fragment Identifier (#frag) */</span> $<span class="token punctuation">{</span>#uris<span class="token punctuation">.</span><span class="token function">escapeFragmentId</span><span class="token punctuation">(</span>uri<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#uris<span class="token punctuation">.</span><span class="token function">escapeFragmentId</span><span class="token punctuation">(</span>uri<span class="token punctuation">,</span> encoding<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#uris<span class="token punctuation">.</span><span class="token function">unescapeFragmentId</span><span class="token punctuation">(</span>uri<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#uris<span class="token punctuation">.</span><span class="token function">unescapeFragmentId</span><span class="token punctuation">(</span>uri<span class="token punctuation">,</span> encoding<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Escape/Unescape as a Query Parameter (?var=value) */</span> $<span class="token punctuation">{</span>#uris<span class="token punctuation">.</span><span class="token function">escapeQueryParam</span><span class="token punctuation">(</span>uri<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#uris<span class="token punctuation">.</span><span class="token function">escapeQueryParam</span><span class="token punctuation">(</span>uri<span class="token punctuation">,</span> encoding<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#uris<span class="token punctuation">.</span><span class="token function">unescapeQueryParam</span><span class="token punctuation">(</span>uri<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#uris<span class="token punctuation">.</span><span class="token function">unescapeQueryParam</span><span class="token punctuation">(</span>uri<span class="token punctuation">,</span> encoding<span class="token punctuation">)</span><span class="token punctuation">}</span></code></pre> </section><section id="conversions" class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="Conversions"><em>21.1.4.</em>Conversions</h3> <ul> <li> <strong>#conversions</strong> : utility object that allows the execution of the <em>Conversion Service</em> at any point of a template:</li> </ul> <pre class=" language-java"><code class=" language-java"><span class="token comment" spellcheck="true">/* * ====================================================================== * See javadoc API for class org.thymeleaf.expression.Conversions * ====================================================================== */</span> <span class="token comment" spellcheck="true">/* * Execute the desired conversion of the 'object' value into the * specified class. */</span> $<span class="token punctuation">{</span>#conversions<span class="token punctuation">.</span><span class="token function">convert</span><span class="token punctuation">(</span>object<span class="token punctuation">,</span> <span class="token string">'java.util.TimeZone'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#conversions<span class="token punctuation">.</span><span class="token function">convert</span><span class="token punctuation">(</span>object<span class="token punctuation">,</span> targetClass<span class="token punctuation">)</span><span class="token punctuation">}</span></code></pre> </section><section id="dates" class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="Dates"><em>21.1.5.</em>Dates</h3> <ul> <li> <strong>#dates</strong> : utility methods for <code>java.util.Date</code> objects:</li> </ul> <pre class=" language-java"><code class=" language-java"><span class="token comment" spellcheck="true">/* * ====================================================================== * See javadoc API for class org.thymeleaf.expression.Dates * ====================================================================== */</span> <span class="token comment" spellcheck="true">/* * Format date with the standard locale format * Also works with arrays, lists or sets */</span> $<span class="token punctuation">{</span>#dates<span class="token punctuation">.</span><span class="token function">format</span><span class="token punctuation">(</span>date<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#dates<span class="token punctuation">.</span><span class="token function">arrayFormat</span><span class="token punctuation">(</span>datesArray<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#dates<span class="token punctuation">.</span><span class="token function">listFormat</span><span class="token punctuation">(</span>datesList<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#dates<span class="token punctuation">.</span><span class="token function">setFormat</span><span class="token punctuation">(</span>datesSet<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Format date with the ISO8601 format * Also works with arrays, lists or sets */</span> $<span class="token punctuation">{</span>#dates<span class="token punctuation">.</span><span class="token function">formatISO</span><span class="token punctuation">(</span>date<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#dates<span class="token punctuation">.</span><span class="token function">arrayFormatISO</span><span class="token punctuation">(</span>datesArray<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#dates<span class="token punctuation">.</span><span class="token function">listFormatISO</span><span class="token punctuation">(</span>datesList<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#dates<span class="token punctuation">.</span><span class="token function">setFormatISO</span><span class="token punctuation">(</span>datesSet<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Format date with the specified pattern * Also works with arrays, lists or sets */</span> $<span class="token punctuation">{</span>#dates<span class="token punctuation">.</span><span class="token function">format</span><span class="token punctuation">(</span>date<span class="token punctuation">,</span> <span class="token string">'dd/MMM/yyyy HH:mm'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#dates<span class="token punctuation">.</span><span class="token function">arrayFormat</span><span class="token punctuation">(</span>datesArray<span class="token punctuation">,</span> <span class="token string">'dd/MMM/yyyy HH:mm'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#dates<span class="token punctuation">.</span><span class="token function">listFormat</span><span class="token punctuation">(</span>datesList<span class="token punctuation">,</span> <span class="token string">'dd/MMM/yyyy HH:mm'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#dates<span class="token punctuation">.</span><span class="token function">setFormat</span><span class="token punctuation">(</span>datesSet<span class="token punctuation">,</span> <span class="token string">'dd/MMM/yyyy HH:mm'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Obtain date properties * Also works with arrays, lists or sets */</span> $<span class="token punctuation">{</span>#dates<span class="token punctuation">.</span><span class="token function">day</span><span class="token punctuation">(</span>date<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also arrayDay(...), listDay(...), etc.</span> $<span class="token punctuation">{</span>#dates<span class="token punctuation">.</span><span class="token function">month</span><span class="token punctuation">(</span>date<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also arrayMonth(...), listMonth(...), etc.</span> $<span class="token punctuation">{</span>#dates<span class="token punctuation">.</span><span class="token function">monthName</span><span class="token punctuation">(</span>date<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also arrayMonthName(...), listMonthName(...), etc.</span> $<span class="token punctuation">{</span>#dates<span class="token punctuation">.</span><span class="token function">monthNameShort</span><span class="token punctuation">(</span>date<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also arrayMonthNameShort(...), listMonthNameShort(...), etc.</span> $<span class="token punctuation">{</span>#dates<span class="token punctuation">.</span><span class="token function">year</span><span class="token punctuation">(</span>date<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also arrayYear(...), listYear(...), etc.</span> $<span class="token punctuation">{</span>#dates<span class="token punctuation">.</span><span class="token function">dayOfWeek</span><span class="token punctuation">(</span>date<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also arrayDayOfWeek(...), listDayOfWeek(...), etc.</span> $<span class="token punctuation">{</span>#dates<span class="token punctuation">.</span><span class="token function">dayOfWeekName</span><span class="token punctuation">(</span>date<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also arrayDayOfWeekName(...), listDayOfWeekName(...), etc.</span> $<span class="token punctuation">{</span>#dates<span class="token punctuation">.</span><span class="token function">dayOfWeekNameShort</span><span class="token punctuation">(</span>date<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also arrayDayOfWeekNameShort(...), listDayOfWeekNameShort(...), etc.</span> $<span class="token punctuation">{</span>#dates<span class="token punctuation">.</span><span class="token function">hour</span><span class="token punctuation">(</span>date<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also arrayHour(...), listHour(...), etc.</span> $<span class="token punctuation">{</span>#dates<span class="token punctuation">.</span><span class="token function">minute</span><span class="token punctuation">(</span>date<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also arrayMinute(...), listMinute(...), etc.</span> $<span class="token punctuation">{</span>#dates<span class="token punctuation">.</span><span class="token function">second</span><span class="token punctuation">(</span>date<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also arraySecond(...), listSecond(...), etc.</span> $<span class="token punctuation">{</span>#dates<span class="token punctuation">.</span><span class="token function">millisecond</span><span class="token punctuation">(</span>date<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also arrayMillisecond(...), listMillisecond(...), etc.</span> <span class="token comment" spellcheck="true">/* * Create date (java.util.Date) objects from its components */</span> $<span class="token punctuation">{</span>#dates<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>year<span class="token punctuation">,</span>month<span class="token punctuation">,</span>day<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#dates<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>year<span class="token punctuation">,</span>month<span class="token punctuation">,</span>day<span class="token punctuation">,</span>hour<span class="token punctuation">,</span>minute<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#dates<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>year<span class="token punctuation">,</span>month<span class="token punctuation">,</span>day<span class="token punctuation">,</span>hour<span class="token punctuation">,</span>minute<span class="token punctuation">,</span>second<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#dates<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>year<span class="token punctuation">,</span>month<span class="token punctuation">,</span>day<span class="token punctuation">,</span>hour<span class="token punctuation">,</span>minute<span class="token punctuation">,</span>second<span class="token punctuation">,</span>millisecond<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Create a date (java.util.Date) object for the current date and time */</span> $<span class="token punctuation">{</span>#dates<span class="token punctuation">.</span><span class="token function">createNow</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#dates<span class="token punctuation">.</span><span class="token function">createNowForTimeZone</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Create a date (java.util.Date) object for the current date (time set to 00:00) */</span> $<span class="token punctuation">{</span>#dates<span class="token punctuation">.</span><span class="token function">createToday</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#dates<span class="token punctuation">.</span><span class="token function">createTodayForTimeZone</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span></code></pre> </section><section id="calendars" class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="Calendars"><em>21.1.6.</em>Calendars</h3> <ul> <li> <strong>#calendars</strong> : analogous to <code>#dates</code>, but for <code>java.util.Calendar</code> objects:</li> </ul> <pre class=" language-java"><code class=" language-java"><span class="token comment" spellcheck="true">/* * ====================================================================== * See javadoc API for class org.thymeleaf.expression.Calendars * ====================================================================== */</span> <span class="token comment" spellcheck="true">/* * Format calendar with the standard locale format * Also works with arrays, lists or sets */</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">format</span><span class="token punctuation">(</span>cal<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">arrayFormat</span><span class="token punctuation">(</span>calArray<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">listFormat</span><span class="token punctuation">(</span>calList<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">setFormat</span><span class="token punctuation">(</span>calSet<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Format calendar with the ISO8601 format * Also works with arrays, lists or sets */</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">formatISO</span><span class="token punctuation">(</span>cal<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">arrayFormatISO</span><span class="token punctuation">(</span>calArray<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">listFormatISO</span><span class="token punctuation">(</span>calList<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">setFormatISO</span><span class="token punctuation">(</span>calSet<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Format calendar with the specified pattern * Also works with arrays, lists or sets */</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">format</span><span class="token punctuation">(</span>cal<span class="token punctuation">,</span> <span class="token string">'dd/MMM/yyyy HH:mm'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">arrayFormat</span><span class="token punctuation">(</span>calArray<span class="token punctuation">,</span> <span class="token string">'dd/MMM/yyyy HH:mm'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">listFormat</span><span class="token punctuation">(</span>calList<span class="token punctuation">,</span> <span class="token string">'dd/MMM/yyyy HH:mm'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">setFormat</span><span class="token punctuation">(</span>calSet<span class="token punctuation">,</span> <span class="token string">'dd/MMM/yyyy HH:mm'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Obtain calendar properties * Also works with arrays, lists or sets */</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">day</span><span class="token punctuation">(</span>date<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also arrayDay(...), listDay(...), etc.</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">month</span><span class="token punctuation">(</span>date<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also arrayMonth(...), listMonth(...), etc.</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">monthName</span><span class="token punctuation">(</span>date<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also arrayMonthName(...), listMonthName(...), etc.</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">monthNameShort</span><span class="token punctuation">(</span>date<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also arrayMonthNameShort(...), listMonthNameShort(...), etc.</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">year</span><span class="token punctuation">(</span>date<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also arrayYear(...), listYear(...), etc.</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">dayOfWeek</span><span class="token punctuation">(</span>date<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also arrayDayOfWeek(...), listDayOfWeek(...), etc.</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">dayOfWeekName</span><span class="token punctuation">(</span>date<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also arrayDayOfWeekName(...), listDayOfWeekName(...), etc.</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">dayOfWeekNameShort</span><span class="token punctuation">(</span>date<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also arrayDayOfWeekNameShort(...), listDayOfWeekNameShort(...), etc.</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">hour</span><span class="token punctuation">(</span>date<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also arrayHour(...), listHour(...), etc.</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">minute</span><span class="token punctuation">(</span>date<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also arrayMinute(...), listMinute(...), etc.</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">second</span><span class="token punctuation">(</span>date<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also arraySecond(...), listSecond(...), etc.</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">millisecond</span><span class="token punctuation">(</span>date<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also arrayMillisecond(...), listMillisecond(...), etc.</span> <span class="token comment" spellcheck="true">/* * Create calendar (java.util.Calendar) objects from its components */</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>year<span class="token punctuation">,</span>month<span class="token punctuation">,</span>day<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>year<span class="token punctuation">,</span>month<span class="token punctuation">,</span>day<span class="token punctuation">,</span>hour<span class="token punctuation">,</span>minute<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>year<span class="token punctuation">,</span>month<span class="token punctuation">,</span>day<span class="token punctuation">,</span>hour<span class="token punctuation">,</span>minute<span class="token punctuation">,</span>second<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>year<span class="token punctuation">,</span>month<span class="token punctuation">,</span>day<span class="token punctuation">,</span>hour<span class="token punctuation">,</span>minute<span class="token punctuation">,</span>second<span class="token punctuation">,</span>millisecond<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">createForTimeZone</span><span class="token punctuation">(</span>year<span class="token punctuation">,</span>month<span class="token punctuation">,</span>day<span class="token punctuation">,</span>timeZone<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">createForTimeZone</span><span class="token punctuation">(</span>year<span class="token punctuation">,</span>month<span class="token punctuation">,</span>day<span class="token punctuation">,</span>hour<span class="token punctuation">,</span>minute<span class="token punctuation">,</span>timeZone<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">createForTimeZone</span><span class="token punctuation">(</span>year<span class="token punctuation">,</span>month<span class="token punctuation">,</span>day<span class="token punctuation">,</span>hour<span class="token punctuation">,</span>minute<span class="token punctuation">,</span>second<span class="token punctuation">,</span>timeZone<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">createForTimeZone</span><span class="token punctuation">(</span>year<span class="token punctuation">,</span>month<span class="token punctuation">,</span>day<span class="token punctuation">,</span>hour<span class="token punctuation">,</span>minute<span class="token punctuation">,</span>second<span class="token punctuation">,</span>millisecond<span class="token punctuation">,</span>timeZone<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Create a calendar (java.util.Calendar) object for the current date and time */</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">createNow</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">createNowForTimeZone</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Create a calendar (java.util.Calendar) object for the current date (time set to 00:00) */</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">createToday</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#calendars<span class="token punctuation">.</span><span class="token function">createTodayForTimeZone</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span></code></pre> </section><section id="numbers" class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="Numbers"><em>21.1.7.</em>Numbers</h3> <ul> <li> <strong>#numbers</strong> : utility methods for number objects:</li> </ul> <pre class=" language-java"><code class=" language-java"><span class="token comment" spellcheck="true">/* * ====================================================================== * See javadoc API for class org.thymeleaf.expression.Numbers * ====================================================================== */</span> <span class="token comment" spellcheck="true">/* * ========================== * Formatting integer numbers * ========================== */</span> <span class="token comment" spellcheck="true">/* * Set minimum integer digits. * Also works with arrays, lists or sets */</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">formatInteger</span><span class="token punctuation">(</span>num<span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">arrayFormatInteger</span><span class="token punctuation">(</span>numArray<span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">listFormatInteger</span><span class="token punctuation">(</span>numList<span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">setFormatInteger</span><span class="token punctuation">(</span>numSet<span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Set minimum integer digits and thousands separator: * 'POINT', 'COMMA', 'WHITESPACE', 'NONE' or 'DEFAULT' (by locale). * Also works with arrays, lists or sets */</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">formatInteger</span><span class="token punctuation">(</span>num<span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">,</span><span class="token string">'POINT'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">arrayFormatInteger</span><span class="token punctuation">(</span>numArray<span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">,</span><span class="token string">'POINT'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">listFormatInteger</span><span class="token punctuation">(</span>numList<span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">,</span><span class="token string">'POINT'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">setFormatInteger</span><span class="token punctuation">(</span>numSet<span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">,</span><span class="token string">'POINT'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * ========================== * Formatting decimal numbers * ========================== */</span> <span class="token comment" spellcheck="true">/* * Set minimum integer digits and (exact) decimal digits. * Also works with arrays, lists or sets */</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">formatDecimal</span><span class="token punctuation">(</span>num<span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">arrayFormatDecimal</span><span class="token punctuation">(</span>numArray<span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">listFormatDecimal</span><span class="token punctuation">(</span>numList<span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">setFormatDecimal</span><span class="token punctuation">(</span>numSet<span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Set minimum integer digits and (exact) decimal digits, and also decimal separator. * Also works with arrays, lists or sets */</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">formatDecimal</span><span class="token punctuation">(</span>num<span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">,</span><span class="token string">'COMMA'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">arrayFormatDecimal</span><span class="token punctuation">(</span>numArray<span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">,</span><span class="token string">'COMMA'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">listFormatDecimal</span><span class="token punctuation">(</span>numList<span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">,</span><span class="token string">'COMMA'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">setFormatDecimal</span><span class="token punctuation">(</span>numSet<span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">,</span><span class="token string">'COMMA'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Set minimum integer digits and (exact) decimal digits, and also thousands and * decimal separator. * Also works with arrays, lists or sets */</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">formatDecimal</span><span class="token punctuation">(</span>num<span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">,</span><span class="token string">'POINT'</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">,</span><span class="token string">'COMMA'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">arrayFormatDecimal</span><span class="token punctuation">(</span>numArray<span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">,</span><span class="token string">'POINT'</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">,</span><span class="token string">'COMMA'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">listFormatDecimal</span><span class="token punctuation">(</span>numList<span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">,</span><span class="token string">'POINT'</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">,</span><span class="token string">'COMMA'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">setFormatDecimal</span><span class="token punctuation">(</span>numSet<span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">,</span><span class="token string">'POINT'</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">,</span><span class="token string">'COMMA'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * ===================== * Formatting currencies * ===================== */</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">formatCurrency</span><span class="token punctuation">(</span>num<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">arrayFormatCurrency</span><span class="token punctuation">(</span>numArray<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">listFormatCurrency</span><span class="token punctuation">(</span>numList<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">setFormatCurrency</span><span class="token punctuation">(</span>numSet<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * ====================== * Formatting percentages * ====================== */</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">formatPercent</span><span class="token punctuation">(</span>num<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">arrayFormatPercent</span><span class="token punctuation">(</span>numArray<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">listFormatPercent</span><span class="token punctuation">(</span>numList<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">setFormatPercent</span><span class="token punctuation">(</span>numSet<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Set minimum integer digits and (exact) decimal digits. */</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">formatPercent</span><span class="token punctuation">(</span>num<span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">arrayFormatPercent</span><span class="token punctuation">(</span>numArray<span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">listFormatPercent</span><span class="token punctuation">(</span>numList<span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">setFormatPercent</span><span class="token punctuation">(</span>numSet<span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * =============== * Utility methods * =============== */</span> <span class="token comment" spellcheck="true">/* * Create a sequence (array) of integer numbers going * from x to y */</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">sequence</span><span class="token punctuation">(</span>from<span class="token punctuation">,</span>to<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#numbers<span class="token punctuation">.</span><span class="token function">sequence</span><span class="token punctuation">(</span>from<span class="token punctuation">,</span>to<span class="token punctuation">,</span>step<span class="token punctuation">)</span><span class="token punctuation">}</span></code></pre> </section><section id="strings" class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="Strings"><em>21.1.8.</em>Strings</h3> <ul> <li> <strong>#strings</strong> : utility methods for <code>String</code> objects:</li> </ul> <pre class=" language-java"><code class=" language-java"><span class="token comment" spellcheck="true">/* * ====================================================================== * See javadoc API for class org.thymeleaf.expression.Strings * ====================================================================== */</span> <span class="token comment" spellcheck="true">/* * Null-safe toString() */</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also array*, list* and set*</span> <span class="token comment" spellcheck="true">/* * Check whether a String is empty (or null). Performs a trim() operation before check * Also works with arrays, lists or sets */</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">isEmpty</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">arrayIsEmpty</span><span class="token punctuation">(</span>nameArr<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">listIsEmpty</span><span class="token punctuation">(</span>nameList<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">setIsEmpty</span><span class="token punctuation">(</span>nameSet<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Perform an 'isEmpty()' check on a string and return it if false, defaulting to * another specified string if true. * Also works with arrays, lists or sets */</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">defaultString</span><span class="token punctuation">(</span>text<span class="token punctuation">,</span><span class="token keyword">default</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">arrayDefaultString</span><span class="token punctuation">(</span>textArr<span class="token punctuation">,</span><span class="token keyword">default</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">listDefaultString</span><span class="token punctuation">(</span>textList<span class="token punctuation">,</span><span class="token keyword">default</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">setDefaultString</span><span class="token punctuation">(</span>textSet<span class="token punctuation">,</span><span class="token keyword">default</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Check whether a fragment is contained in a String * Also works with arrays, lists or sets */</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span><span class="token string">'ez'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also array*, list* and set*</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">containsIgnoreCase</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span><span class="token string">'ez'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also array*, list* and set*</span> <span class="token comment" spellcheck="true">/* * Check whether a String starts or ends with a fragment * Also works with arrays, lists or sets */</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">startsWith</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span><span class="token string">'Don'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also array*, list* and set*</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">endsWith</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span>endingFragment<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also array*, list* and set*</span> <span class="token comment" spellcheck="true">/* * Substring-related operations * Also works with arrays, lists or sets */</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span>frag<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also array*, list* and set*</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">,</span><span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also array*, list* and set*</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">substringAfter</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span>prefix<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also array*, list* and set*</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">substringBefore</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span>suffix<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also array*, list* and set*</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span><span class="token string">'las'</span><span class="token punctuation">,</span><span class="token string">'ler'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also array*, list* and set*</span> <span class="token comment" spellcheck="true">/* * Append and prepend * Also works with arrays, lists or sets */</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">prepend</span><span class="token punctuation">(</span>str<span class="token punctuation">,</span>prefix<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also array*, list* and set*</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span>str<span class="token punctuation">,</span>suffix<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also array*, list* and set*</span> <span class="token comment" spellcheck="true">/* * Change case * Also works with arrays, lists or sets */</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">toUpperCase</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also array*, list* and set*</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">toLowerCase</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also array*, list* and set*</span> <span class="token comment" spellcheck="true">/* * Split and join */</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">arrayJoin</span><span class="token punctuation">(</span>namesArray<span class="token punctuation">,</span><span class="token string">','</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">listJoin</span><span class="token punctuation">(</span>namesList<span class="token punctuation">,</span><span class="token string">','</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">setJoin</span><span class="token punctuation">(</span>namesSet<span class="token punctuation">,</span><span class="token string">','</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">arraySplit</span><span class="token punctuation">(</span>namesStr<span class="token punctuation">,</span><span class="token string">','</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// returns String[]</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">listSplit</span><span class="token punctuation">(</span>namesStr<span class="token punctuation">,</span><span class="token string">','</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// returns List<String></span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">setSplit</span><span class="token punctuation">(</span>namesStr<span class="token punctuation">,</span><span class="token string">','</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// returns Set<String></span> <span class="token comment" spellcheck="true">/* * Trim * Also works with arrays, lists or sets */</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">trim</span><span class="token punctuation">(</span>str<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also array*, list* and set*</span> <span class="token comment" spellcheck="true">/* * Compute length * Also works with arrays, lists or sets */</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span>str<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also array*, list* and set*</span> <span class="token comment" spellcheck="true">/* * Abbreviate text making it have a maximum size of n. If text is bigger, it * will be clipped and finished in "..." * Also works with arrays, lists or sets */</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">abbreviate</span><span class="token punctuation">(</span>str<span class="token punctuation">,</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also array*, list* and set*</span> <span class="token comment" spellcheck="true">/* * Convert the first character to upper-case (and vice-versa) */</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">capitalize</span><span class="token punctuation">(</span>str<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also array*, list* and set*</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">unCapitalize</span><span class="token punctuation">(</span>str<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also array*, list* and set*</span> <span class="token comment" spellcheck="true">/* * Convert the first character of every word to upper-case */</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">capitalizeWords</span><span class="token punctuation">(</span>str<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also array*, list* and set*</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">capitalizeWords</span><span class="token punctuation">(</span>str<span class="token punctuation">,</span>delimiters<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also array*, list* and set*</span> <span class="token comment" spellcheck="true">/* * Escape the string */</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">escapeXml</span><span class="token punctuation">(</span>str<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also array*, list* and set*</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">escapeJava</span><span class="token punctuation">(</span>str<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also array*, list* and set*</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">escapeJavaScript</span><span class="token punctuation">(</span>str<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also array*, list* and set*</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">unescapeJava</span><span class="token punctuation">(</span>str<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also array*, list* and set*</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">unescapeJavaScript</span><span class="token punctuation">(</span>str<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// also array*, list* and set*</span> <span class="token comment" spellcheck="true">/* * Null-safe comparison and concatenation */</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">equals</span><span class="token punctuation">(</span>first<span class="token punctuation">,</span> second<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">equalsIgnoreCase</span><span class="token punctuation">(</span>first<span class="token punctuation">,</span> second<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">concat</span><span class="token punctuation">(</span>values<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">concatReplaceNulls</span><span class="token punctuation">(</span>nullValue<span class="token punctuation">,</span> values<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Random */</span> $<span class="token punctuation">{</span>#strings<span class="token punctuation">.</span><span class="token function">randomAlphanumeric</span><span class="token punctuation">(</span>count<span class="token punctuation">)</span><span class="token punctuation">}</span></code></pre> </section><section id="objects" class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="Objects"><em>21.1.9.</em>Objects</h3> <ul> <li> <strong>#objects</strong> : utility methods for objects in general</li> </ul> <pre class=" language-java"><code class=" language-java"><span class="token comment" spellcheck="true">/* * ====================================================================== * See javadoc API for class org.thymeleaf.expression.Objects * ====================================================================== */</span> <span class="token comment" spellcheck="true">/* * Return obj if it is not null, and default otherwise * Also works with arrays, lists or sets */</span> $<span class="token punctuation">{</span>#objects<span class="token punctuation">.</span><span class="token function">nullSafe</span><span class="token punctuation">(</span>obj<span class="token punctuation">,</span><span class="token keyword">default</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#objects<span class="token punctuation">.</span><span class="token function">arrayNullSafe</span><span class="token punctuation">(</span>objArray<span class="token punctuation">,</span><span class="token keyword">default</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#objects<span class="token punctuation">.</span><span class="token function">listNullSafe</span><span class="token punctuation">(</span>objList<span class="token punctuation">,</span><span class="token keyword">default</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#objects<span class="token punctuation">.</span><span class="token function">setNullSafe</span><span class="token punctuation">(</span>objSet<span class="token punctuation">,</span><span class="token keyword">default</span><span class="token punctuation">)</span><span class="token punctuation">}</span></code></pre> </section><section id="booleans" class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="Booleans"><em>21.1.10.</em>Booleans</h3> <ul> <li> <strong>#bools</strong> : utility methods for boolean evaluation</li> </ul> <pre class=" language-java"><code class=" language-java"><span class="token comment" spellcheck="true">/* * ====================================================================== * See javadoc API for class org.thymeleaf.expression.Bools * ====================================================================== */</span> <span class="token comment" spellcheck="true">/* * Evaluate a condition in the same way that it would be evaluated in a th:if tag * (see conditional evaluation chapter afterwards). * Also works with arrays, lists or sets */</span> $<span class="token punctuation">{</span>#bools<span class="token punctuation">.</span><span class="token function">isTrue</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#bools<span class="token punctuation">.</span><span class="token function">arrayIsTrue</span><span class="token punctuation">(</span>objArray<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#bools<span class="token punctuation">.</span><span class="token function">listIsTrue</span><span class="token punctuation">(</span>objList<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#bools<span class="token punctuation">.</span><span class="token function">setIsTrue</span><span class="token punctuation">(</span>objSet<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Evaluate with negation * Also works with arrays, lists or sets */</span> $<span class="token punctuation">{</span>#bools<span class="token punctuation">.</span><span class="token function">isFalse</span><span class="token punctuation">(</span>cond<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#bools<span class="token punctuation">.</span><span class="token function">arrayIsFalse</span><span class="token punctuation">(</span>condArray<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#bools<span class="token punctuation">.</span><span class="token function">listIsFalse</span><span class="token punctuation">(</span>condList<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#bools<span class="token punctuation">.</span><span class="token function">setIsFalse</span><span class="token punctuation">(</span>condSet<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Evaluate and apply AND operator * Receive an array, a list or a set as parameter */</span> $<span class="token punctuation">{</span>#bools<span class="token punctuation">.</span><span class="token function">arrayAnd</span><span class="token punctuation">(</span>condArray<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#bools<span class="token punctuation">.</span><span class="token function">listAnd</span><span class="token punctuation">(</span>condList<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#bools<span class="token punctuation">.</span><span class="token function">setAnd</span><span class="token punctuation">(</span>condSet<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Evaluate and apply OR operator * Receive an array, a list or a set as parameter */</span> $<span class="token punctuation">{</span>#bools<span class="token punctuation">.</span><span class="token function">arrayOr</span><span class="token punctuation">(</span>condArray<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#bools<span class="token punctuation">.</span><span class="token function">listOr</span><span class="token punctuation">(</span>condList<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#bools<span class="token punctuation">.</span><span class="token function">setOr</span><span class="token punctuation">(</span>condSet<span class="token punctuation">)</span><span class="token punctuation">}</span></code></pre> </section><section id="arrays" class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="Arrays"><em>21.1.11.</em>Arrays</h3> <ul> <li> <strong>#arrays</strong> : utility methods for arrays</li> </ul> <pre class=" language-java"><code class=" language-java"><span class="token comment" spellcheck="true">/* * ====================================================================== * See javadoc API for class org.thymeleaf.expression.Arrays * ====================================================================== */</span> <span class="token comment" spellcheck="true">/* * Converts to array, trying to infer array component class. * Note that if resulting array is empty, or if the elements * of the target object are not all of the same class, * this method will return Object[]. */</span> $<span class="token punctuation">{</span>#arrays<span class="token punctuation">.</span><span class="token function">toArray</span><span class="token punctuation">(</span>object<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Convert to arrays of the specified component class. */</span> $<span class="token punctuation">{</span>#arrays<span class="token punctuation">.</span><span class="token function">toStringArray</span><span class="token punctuation">(</span>object<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#arrays<span class="token punctuation">.</span><span class="token function">toIntegerArray</span><span class="token punctuation">(</span>object<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#arrays<span class="token punctuation">.</span><span class="token function">toLongArray</span><span class="token punctuation">(</span>object<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#arrays<span class="token punctuation">.</span><span class="token function">toDoubleArray</span><span class="token punctuation">(</span>object<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#arrays<span class="token punctuation">.</span><span class="token function">toFloatArray</span><span class="token punctuation">(</span>object<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#arrays<span class="token punctuation">.</span><span class="token function">toBooleanArray</span><span class="token punctuation">(</span>object<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Compute length */</span> $<span class="token punctuation">{</span>#arrays<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span>array<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Check whether array is empty */</span> $<span class="token punctuation">{</span>#arrays<span class="token punctuation">.</span><span class="token function">isEmpty</span><span class="token punctuation">(</span>array<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Check if element or elements are contained in array */</span> $<span class="token punctuation">{</span>#arrays<span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span>array<span class="token punctuation">,</span> element<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#arrays<span class="token punctuation">.</span><span class="token function">containsAll</span><span class="token punctuation">(</span>array<span class="token punctuation">,</span> elements<span class="token punctuation">)</span><span class="token punctuation">}</span></code></pre> </section><section id="lists" class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="Lists"><em>21.1.12.</em>Lists</h3> <ul> <li> <strong>#lists</strong> : utility methods for lists</li> </ul> <pre class=" language-java"><code class=" language-java"><span class="token comment" spellcheck="true">/* * ====================================================================== * See javadoc API for class org.thymeleaf.expression.Lists * ====================================================================== */</span> <span class="token comment" spellcheck="true">/* * Converts to list */</span> $<span class="token punctuation">{</span>#lists<span class="token punctuation">.</span><span class="token function">toList</span><span class="token punctuation">(</span>object<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Compute size */</span> $<span class="token punctuation">{</span>#lists<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span>list<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Check whether list is empty */</span> $<span class="token punctuation">{</span>#lists<span class="token punctuation">.</span><span class="token function">isEmpty</span><span class="token punctuation">(</span>list<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Check if element or elements are contained in list */</span> $<span class="token punctuation">{</span>#lists<span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span>list<span class="token punctuation">,</span> element<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#lists<span class="token punctuation">.</span><span class="token function">containsAll</span><span class="token punctuation">(</span>list<span class="token punctuation">,</span> elements<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Sort a copy of the given list. The members of the list must implement * comparable or you must define a comparator. */</span> $<span class="token punctuation">{</span>#lists<span class="token punctuation">.</span><span class="token function">sort</span><span class="token punctuation">(</span>list<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#lists<span class="token punctuation">.</span><span class="token function">sort</span><span class="token punctuation">(</span>list<span class="token punctuation">,</span> comparator<span class="token punctuation">)</span><span class="token punctuation">}</span></code></pre> </section><section id="sets" class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="Sets"><em>21.1.13.</em>Sets</h3> <ul> <li> <strong>#sets</strong> : utility methods for sets</li> </ul> <pre class=" language-java"><code class=" language-java"><span class="token comment" spellcheck="true">/* * ====================================================================== * See javadoc API for class org.thymeleaf.expression.Sets * ====================================================================== */</span> <span class="token comment" spellcheck="true">/* * Converts to set */</span> $<span class="token punctuation">{</span>#sets<span class="token punctuation">.</span><span class="token function">toSet</span><span class="token punctuation">(</span>object<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Compute size */</span> $<span class="token punctuation">{</span>#sets<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span>set<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Check whether set is empty */</span> $<span class="token punctuation">{</span>#sets<span class="token punctuation">.</span><span class="token function">isEmpty</span><span class="token punctuation">(</span>set<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Check if element or elements are contained in set */</span> $<span class="token punctuation">{</span>#sets<span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span>set<span class="token punctuation">,</span> element<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#sets<span class="token punctuation">.</span><span class="token function">containsAll</span><span class="token punctuation">(</span>set<span class="token punctuation">,</span> elements<span class="token punctuation">)</span><span class="token punctuation">}</span></code></pre> </section><section id="maps" class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="Maps"><em>21.1.14.</em>Maps</h3> <ul> <li> <strong>#maps</strong> : utility methods for maps</li> </ul> <pre class=" language-java"><code class=" language-java"><span class="token comment" spellcheck="true">/* * ====================================================================== * See javadoc API for class org.thymeleaf.expression.Maps * ====================================================================== */</span> <span class="token comment" spellcheck="true">/* * Compute size */</span> $<span class="token punctuation">{</span>#maps<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span>map<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Check whether map is empty */</span> $<span class="token punctuation">{</span>#maps<span class="token punctuation">.</span><span class="token function">isEmpty</span><span class="token punctuation">(</span>map<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Check if key/s or value/s are contained in maps */</span> $<span class="token punctuation">{</span>#maps<span class="token punctuation">.</span><span class="token function">containsKey</span><span class="token punctuation">(</span>map<span class="token punctuation">,</span> key<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#maps<span class="token punctuation">.</span><span class="token function">containsAllKeys</span><span class="token punctuation">(</span>map<span class="token punctuation">,</span> keys<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#maps<span class="token punctuation">.</span><span class="token function">containsValue</span><span class="token punctuation">(</span>map<span class="token punctuation">,</span> value<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#maps<span class="token punctuation">.</span><span class="token function">containsAllValues</span><span class="token punctuation">(</span>map<span class="token punctuation">,</span> value<span class="token punctuation">)</span><span class="token punctuation">}</span></code></pre> </section><section id="aggregates" class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="Aggregates"><em>21.1.15.</em>Aggregates</h3> <ul> <li> <strong>#aggregates</strong> : utility methods for creating aggregates on arrays or collections</li> </ul> <pre class=" language-java"><code class=" language-java"><span class="token comment" spellcheck="true">/* * ====================================================================== * See javadoc API for class org.thymeleaf.expression.Aggregates * ====================================================================== */</span> <span class="token comment" spellcheck="true">/* * Compute sum. Returns null if array or collection is empty */</span> $<span class="token punctuation">{</span>#aggregates<span class="token punctuation">.</span><span class="token function">sum</span><span class="token punctuation">(</span>array<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#aggregates<span class="token punctuation">.</span><span class="token function">sum</span><span class="token punctuation">(</span>collection<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Compute average. Returns null if array or collection is empty */</span> $<span class="token punctuation">{</span>#aggregates<span class="token punctuation">.</span><span class="token function">avg</span><span class="token punctuation">(</span>array<span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#aggregates<span class="token punctuation">.</span><span class="token function">avg</span><span class="token punctuation">(</span>collection<span class="token punctuation">)</span><span class="token punctuation">}</span></code></pre> </section><section id="ids" class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="IDs"><em>21.1.16.</em>IDs</h3> <ul> <li> <strong>#ids</strong> : utility methods for dealing with <code>id</code> attributes that might be repeated (for example, as a result of an iteration).</li> </ul> <pre class=" language-java"><code class=" language-java"><span class="token comment" spellcheck="true">/* * ====================================================================== * See javadoc API for class org.thymeleaf.expression.Ids * ====================================================================== */</span> <span class="token comment" spellcheck="true">/* * Normally used in th:id attributes, for appending a counter to the id attribute value * so that it remains unique even when involved in an iteration process. */</span> $<span class="token punctuation">{</span>#ids<span class="token punctuation">.</span><span class="token function">seq</span><span class="token punctuation">(</span><span class="token string">'someId'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/* * Normally used in th:for attributes in <label> tags, so that these labels can refer to Ids * generated by means if the #ids.seq(...) function. * * Depending on whether the <label> goes before or after the element with the #ids.seq(...) * function, the "next" (label goes before "seq") or the "prev" function (label goes after * "seq") function should be called. */</span> $<span class="token punctuation">{</span>#ids<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token string">'someId'</span><span class="token punctuation">)</span><span class="token punctuation">}</span> $<span class="token punctuation">{</span>#ids<span class="token punctuation">.</span><span class="token function">prev</span><span class="token punctuation">(</span><span class="token string">'someId'</span><span class="token punctuation">)</span><span class="token punctuation">}</span></code></pre> </section></section><section id="appendix-c-markup-selector-syntax" class="level1"> <h1 class="content-index-heading content-index-heading-level-1" name="" id="20 Appendix C: Markup Selector Syntax"><em>22.</em>20 Appendix C: Markup Selector Syntax</h1> Thymeleaf’s Markup Selectors are directly borrowed from Thymeleaf’s parsing library: <a href="http://www.75271.com/go.html/" target="_blank" rel="nofollow">AttoParser</a>. The syntax for this selectors has large similarities with that of selectors in XPath, CSS and jQuery, which makes them easy to use for most users. You can have a look at the complete syntax reference at the <a href="http://www.75271.com/go.html/" target="_blank" rel="nofollow">AttoParser documentation</a>. For example, the following selector will select every <code><div></code> with the class <code>content</code>, in every position inside the markup (note this is not as concise as it could be, read on to know why): <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name"><span class="token namespace">th:</span>insert</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>mytemplate :: //div[@class<span class="token punctuation">=</span><span class="token punctuation">'</span>content<span class="token punctuation">'</span>]<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre> The basic syntax includes: <ul> <li> <code>/x</code> means direct children of the current node with name x.</li> <li> <code>//x</code> means children of the current node with name x, at any depth.</li> <li> <code>x[@z="v"]</code> means elements with name x and an attribute called z with value “v”.</li> <li> <code>x[@z1="v1" and @z2="v2"]</code> means elements with name x and attributes z1 and z2 with values “v1” and “v2”, respectively.</li> <li> <code>x[i]</code> means element with name x positioned in number i among its siblings.</li> <li> <code>x[@z="v"][i]</code> means elements with name x, attribute z with value “v” and positioned in number i among its siblings that also match this condition.</li> </ul> But more concise syntax can also be used: <ul> <li> <code>x</code> is exactly equivalent to <code>//x</code> (search an element with name or reference <code>x</code> at any depth level, a <em>reference</em> being a <code>th:ref</code> or a <code>th:fragment</code> attribute).</li> <li>Selectors are also allowed without element name/reference, as long as they include a specification of arguments. So <code>[@class='oneclass']</code> is a valid selector that looks for any elements (tags) with a class attribute with value <code>"oneclass"</code>.</li> </ul> Advanced attribute selection features: <ul> <li>Besides <code>=</code> (equal), other comparison operators are also valid: <code>!=</code> (not equal), <code>^=</code> (starts with) and <code>$=</code> (ends with). For example: <code>x[@class^='section']</code> means elements with name <code>x</code> and a value for attribute <code>class</code> that starts with <code>section</code>.</li> <li>Attributes can be specified both starting with <code>@</code> (XPath-style) and without (jQuery-style). So <code>x[z='v']</code> is equivalent to <code>x[@z='v']</code>.</li> <li>Multiple-attribute modifiers can be joined both with <code>and</code> (XPath-style) and also by chaining multiple modifiers (jQuery-style). So <code>x[@z1='v1' and @z2='v2']</code> is actually equivalent to <code>x[@z1='v1'][@z2='v2']</code> (and also to <code>x[z1='v1'][z2='v2']</code>).</li> </ul> Direct <em>jQuery-like</em> selectors: <ul> <li> <code>x.oneclass</code> is equivalent to <code>x[class='oneclass']</code>.</li> <li> <code>.oneclass</code> is equivalent to <code>[class='oneclass']</code>.</li> <li> <code>x#oneid</code> is equivalent to <code>x[id='oneid']</code>.</li> <li> <code>#oneid</code> is equivalent to <code>[id='oneid']</code>.</li> <li> <code>x%oneref</code> means <code><x></code> tags that have a <code>th:ref="oneref"</code> or <code>th:fragment="oneref"</code> attribute.</li> <li> <code>%oneref</code> means any tags that have a <code>th:ref="oneref"</code> or <code>th:fragment="oneref"</code> attribute. Note this is actually equivalent to simply <code>oneref</code> because references can be used instead of element names.</li> <li>Direct selectors and attribute selectors can be mixed: <code>a.external[@href^='https']</code>.</li> </ul> So the above Markup Selector expression: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name"><span class="token namespace">th:</span>insert</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>mytemplate :: //div[@class<span class="token punctuation">=</span><span class="token punctuation">'</span>content<span class="token punctuation">'</span>]<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre> Could be written as: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name"><span class="token namespace">th:</span>insert</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>mytemplate :: div.content<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre> Examining a different example, this: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name"><span class="token namespace">th:</span>replace</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>mytemplate :: myfrag<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre> Will look for a <code>th:fragment="myfrag"</code> fragment signature (or <code>th:ref</code> references). But would also look for tags with name <code>myfrag</code> if they existed (which they don’t, in HTML). Note the difference with: <pre class=" language-html"><code class=" language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name"><span class="token namespace">th:</span>replace</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>mytemplate :: .myfrag<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre> …which will actually look for any elements with <code>class="myfrag"</code>, without caring about <code>th:fragment</code> signatures (or <code>th:ref</code> references). <section id="multivalued-class-matching" class="level3"> <h3 class="content-index-heading content-index-heading-level-3" name="" id="Multivalued class matching"><em>22.1.1.</em>Multivalued class matching</h3> Markup Selectors understand the class attribute to be <strong>multivalued</strong>, and therefore allow the application of selectors on this attribute even if the element has several class values. For example, <code>div.two</code> will match <code><div class="one two three" /></code> </section></section> </div> </div><hr><div align="center" class="open-message"><i class="fa fa-bullhorn"></i>开心洋葱 , 版权所有丨如未注明 , 均为原创丨未经授权请勿修改 , 转载请注明<a href="http://www.75271.com/20087.html" target="_blank" title="Thymeleaf中文手册-语法速查手册-中文帮助手册">Thymeleaf中文手册-语法速查手册-中文帮助手册</a>!</div> <div class="article-social"> <a href="javascript:;" data-action="ding" data-id="20087" id="Addlike" class="action"><i class="fa fa-heart-o"></i>喜欢 (<span class="count">0</span>)</a><span class="or"><style>.article-social .weixin:hover{background:#fff;}</style><a class="weixin" style="border-bottom:0px;font-size:15pt;cursor:pointer;">赏<div class="weixin-popover"><div class="popover bottom in"><div class="arrow"></div><div class="popover-title"><center>[开心洋葱]</center></div><div class="popover-content"><img width="200px" height="200px" src="http://tfsimg.alipay.com/images/mobilecodec/T1YYxoXlxcXXXXXXXX"></div></div></div></a></span><span class="action action-share bdsharebuttonbox"><i class="fa fa-share-alt"></i>分享 (<span class="bds_count" data-cmd="count" title="累计分享0次">0</span>)<div class="action-popover"><div class="popover top in"><div class="arrow"></div><div class="popover-content"><a href="#" class="sinaweibo fa fa-weibo" data-cmd="tsina" title="分享到新浪微博"></a><a href="#" class="bds_qzone fa fa-star" data-cmd="qzone" title="分享到QQ空间"></a><a href="#" class="tencentweibo fa fa-tencent-weibo" data-cmd="tqq" title="分享到腾讯微博"></a><a href="#" class="qq fa fa-qq" data-cmd="sqq" title="分享到QQ好友"></a><a href="#" class="bds_renren fa fa-renren" data-cmd="renren" title="分享到人人网"></a><a href="#" class="bds_weixin fa fa-weixin" data-cmd="weixin" title="分享到微信"></a><a href="#" class="bds_more fa fa-ellipsis-h" data-cmd="more"></a></div></div></div></span></div> </article> <footer class="article-footer"> <div class="article-tags"><i class="fa fa-tags"></i><a href="http://www.75271.com/tag/thymeleaf" rel="tag">thymeleaf</a><a href="http://www.75271.com/tag/%e4%b8%ad%e6%96%87%e6%89%8b%e5%86%8c" rel="tag">中文手册</a><a href="http://www.75271.com/tag/%e6%a8%a1%e6%9d%bf%e5%bc%95%e6%93%8e" rel="tag">模板引擎</a></div></footer> <nav class="article-nav"> <span class="article-nav-prev"><i class="fa fa-angle-double-left"></i> <a href="http://www.75271.com/20155.html" rel="prev">英语固定搭配(很全哦!不看别后悔)</a></span> <span class="article-nav-next"><a href="http://www.75271.com/20164.html" rel="next">开心洋葱 会员 充值大优惠!!</a> <i class="fa fa-angle-double-right"></i></span> </nav><div class="sg-author clr"><div class="img"><img alt="" src="https://secure.gravatar.com/avatar/db78161098205537fe1b2415712da0b7?s=512&d=wavatar&r=g" srcset="https://secure.gravatar.com/avatar/db78161098205537fe1b2415712da0b7?s=1024&d=wavatar&r=g 2x" class="avatar avatar-512 photo" height="512" width="512"></div><div class="sg-author-info"><div class="word"><div class="wordname">关于作者:<a href="http://www.75271.com/author/admin" title="由开心洋葱发布" rel="author">开心洋葱</a></div><div class="authordes">开心洋葱,开心洋葱头,水墨</div><div class="authorsocial"><span class="social-icon-wrap"><a class="as-img as-home" target="_blank" href="http://www.75271.com" title="作者主页"><i class="fa fa-home"></i>作者主页</a></span><span class="social-icon-wrap"><a id="showdiv" class="as-img as-donate" target="_blank" href="#donatecoffee"> <i class="fa fa-coffee"></i>赞助作者 </a></span><span class="social-icon-wrap"><a class="as-img as-email" target="_blank" href="mailto:431910788@qq.com" title="给我写信"><i class="fa fa-envelope"></i></a></span><span class="social-icon-wrap"><a class="as-img as-qq" target="_blank" href="tencent://message/?uin=431910788&Site=&Menu=yes" title="QQ交谈"><i class="fa fa-qq"></i></a></span></div></div></div></div><div id="donatecoffee" style="overflow:auto;display:none;"><img width="400px" height="400px" src="http://tfsimg.alipay.com/images/mobilecodec/T1YYxoXlxcXXXXXXXX"></div> <div class="related_top"> <div class="related_posts"><ul class="related_img"> <li class="related_box"> <a href="http://www.75271.com/20097.html" title="SpringBoot热部署(Class+Thymeleaf)" target="_blank"><img class="thumb" style="width:185px;height:110px" src="http://www.75271.com/wp-content/themes/com75271/timthumb.php?src=http://www.75271.com/wp-content/themes/com75271/css/img/pic/10.jpg&h=110&w=185&q=90&zc=1&ct=1" alt="SpringBoot热部署(Class+Thymeleaf)"><br><span class="r_title">SpringBoot热部署(Class+Thymeleaf)</span></a> </li> <li class="related_box"> <a href="http://www.75271.com/20088.html" title="Thymeleaf中文手册-中文语法手册-中文帮助手册" target="_blank"><img class="thumb" style="width:185px;height:110px" src="http://www.75271.com/wp-content/themes/com75271/timthumb.php?src=http://www.75271.com/wp-content/themes/com75271/css/img/pic/10.jpg&h=110&w=185&q=90&zc=1&ct=1" alt="Thymeleaf中文手册-中文语法手册-中文帮助手册"><br><span class="r_title">Thymeleaf中文手册-中文语法手册-中文帮助手册</span></a> </li> <li class="related_box"> <a href="http://www.75271.com/20045.html" title="velocity中文用户手册-用户开发指南" target="_blank"><img class="thumb" style="width:185px;height:110px" src="http://www.75271.com/wp-content/themes/com75271/timthumb.php?src=http://www.75271.com/wp-content/themes/com75271/css/img/pic/10.jpg&h=110&w=185&q=90&zc=1&ct=1" alt="velocity中文用户手册-用户开发指南"><br><span class="r_title">velocity中文用户手册-用户开发指南</span></a> </li> <li class="related_box"> <a href="http://www.75271.com/20197.html" title="免费的vpn配置 -开心洋葱官网" target="_blank"><img class="thumb" style="width:185px;height:110px" src="http://www.75271.com/wp-content/themes/com75271/timthumb.php?src=http://www.75271.com/wp-content/themes/com75271/css/img/pic/10.jpg&h=110&w=185&q=90&zc=1&ct=1" alt="免费的vpn配置 -开心洋葱官网"><br><span class="r_title">免费的vpn配置 -开心洋葱官网</span></a> </li> </ul><div class="relates"><ul><li><i class="fa fa-minus"></i><a target="_blank" href="http://www.75271.com/20197.html">免费的vpn配置 -开心洋葱官网</a></li><li><i class="fa fa-minus"></i><a target="_blank" href="http://www.75271.com/20155.html">英语固定搭配(很全哦!不看别后悔)</a></li><li><i class="fa fa-minus"></i><a target="_blank" href="http://www.75271.com/20153.html">抖音最低价刷赞网址,快手低价代刷粉丝</a></li><li><i class="fa fa-minus"></i><a target="_blank" href="http://www.75271.com/20110.html">红心浏览器下载,Redcore安装包,红心办公浏览器,云适配红心企业浏览器 百度云网盘下载地址</a></li><li><i class="fa fa-minus"></i><a target="_blank" href="http://www.75271.com/20022.html">以太坊矿池源码-包含矿池部署及全套区块链教程</a></li><li><i class="fa fa-minus"></i><a target="_blank" href="http://www.75271.com/20019.html">比特币矿池源码下载-矿池包含安装、部署教程全套</a></li><li><i class="fa fa-minus"></i><a target="_blank" href="http://www.75271.com/19939.html">测试动态添加的元素 绑定jQuery绑定on事件</a></li><li><i class="fa fa-minus"></i><a target="_blank" href="http://www.75271.com/19937.html">Beautiful Soup 4.2.0 文档</a></li><li><i class="fa fa-minus"></i><a target="_blank" href="http://www.75271.com/19941.html">java 堆内存运行原理图测试代码</a></li><li><i class="fa fa-minus"></i><a target="_blank" href="http://www.75271.com/19893.html">cocos firefox protocol error: received error packet: invalid length: HTTP/1.0 400 Bad Request</a></li><li><i class="fa fa-minus"></i><a target="_blank" href="http://www.75271.com/19855.html">Git常见命令整理 + 注释(手记)</a></li><li><i class="fa fa-minus"></i><a target="_blank" href="http://www.75271.com/20001.html">Spring Boot 和RocketMQ 继承实战记录</a></li><li><i class="fa fa-minus"></i><a target="_blank" href="http://www.75271.com/19748.html">unity3d 2017.3.0f3破解补丁文件</a></li><li><i class="fa fa-minus"></i><a target="_blank" href="http://www.75271.com/19730.html">windows10 定时获取锁屏壁纸PowerShell脚本</a></li><li><i class="fa fa-minus"></i><a target="_blank" href="http://www.75271.com/19688.html">xcode project.pbxproj文件的介绍</a></li><li><i class="fa fa-minus"></i><a target="_blank" href="http://www.75271.com/19669.html">Xcode9 IOS 真机如何安装 WebDriverAgent自动化测试</a></li></ul></div></div> </div> <div id="comment-ad" class="banner banner-related"><script type="text/javascript"> /*960*90 创建于 2017-6-15*/ var cpro_id = "u3008144";</script><script type="text/javascript" src="http://cpro.baidustatic.com/cpro/ui/c.js"></script></div> <div id="respond" class="no_webshot"> <h3 class="queryinfo"> 您必须 <a href="http://www.75271.com/wp-login.php">登录</a> 才能发表评论! </h3> </div> <div class="banner banner-comment"><script type="text/javascript"> /*自适应 创建于 2017-6-15*/ var cpro_id = "u3008138";</script><script type="text/javascript" src="http://cpro.baidustatic.com/cpro/ui/i.js"></script></div> </div></div><aside class="sidebar"><div class="widget_text widget widget_custom_html"><div class="textwidget custom-html-widget"><div style="height:225px;padding:20px;"><script type="text/javascript"> (function() { var s = "_" + Math.random().toString(36).slice(2); document.write('<div style="" id="' + s + '"><\/script></div>'); (window.slotbydup = window.slotbydup || []).push({ id: "u3801486", container: s }); })();<!-- 多条广告如下脚本只需引入一次 --><script type="text/javascript" src="//cpro.baidustatic.com/cpro/ui/c.js" async="async" defer></script> </div></div><div class="widget_text widget widget_custom_html"><div class="title"><h2>轻松一下</h2></div><div class="textwidget custom-html-widget"><iframe frameborder="no" border="0" marginwidth="0" marginheight="0" width="370" height="110" src="//music.163.com/outchain/player?type=0&id=2443343078&auto=0&height=90"></iframe></div></div><div class="widget widget_text"><div class="title"><h2>搜索</h2></div> <div class="textwidget"><script> function searchbaidu(){ var tar = document.getElementById("baiduform");tar.action="http://zhannei.baidu.com/cse/search?q=&s=5433306801287915407"; tar.submit(); }</script><form id="baiduform" method="get" target="_blank"><input style="margin-left:15px;border-radius:10px;width:190px" type="text" name="q" placeholder="请输入关键词"><input type="hidden" name="s" value="5433306801287915407"><input style="margin-left:10px;border-radius:10px;" type="button" onclick="searchbaidu();" value="搜索"></form></div> </div><div class="widget git_social"><div class="widget widget_text"><div class="textwidget"><div class="social"><a href="http://weibo.com/5902571678/info" rel="external nofollow" title="新浪微博" target="_blank"><i class="sinaweibo fa fa-weibo"></i></a><a href="http://t.qq.com/fmxxjd" rel="external nofollow" title="腾讯微博" target="_blank"><i class="tencentweibo fa fa-tencent-weibo"></i></a><a href="https://github.com/daimakuai/" rel="external nofollow" title="GIT系统" target="_blank"><i class="git fa fa-git"></i></a><a class="weixin"><i class="weixins fa fa-weixin"></i><div class="weixin-popover"><div class="popover bottom in"><div class="arrow"></div><div class="popover-title">订阅号“ispm25”</div><div class="popover-content"><img width="200px" height="200px" src="http://images.75271.com/wp-content/uploads/2016/05/2016050203525284.jpg"></div></div></div></a><a class="weixin"><i class="pay fa fa-paypal"></i><div class="weixin-popover"><div class="popover bottom in"><div class="arrow"></div><div class="popover-title">支付宝“开心洋葱”</div><div class="popover-content"><img width="200px" height="200px" src="http://tfsimg.alipay.com/images/mobilecodec/T1YYxoXlxcXXXXXXXX"></div></div></div></a><a href="tencent://message/?uin=431910788&Site=&Menu=yes%20" rel="external nofollow" title="联系QQ" target="_blank"><i class="qq fa fa-qq"></i></a></div></div></div></div><div class="widget widget_text"><div class="title"><h2>赞助商广告</h2></div> <div class="textwidget"><div style="padding-left:19px"><script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script><!-- 内容页图片300x250, 创建于 11-7-26 --><ins class="adsbygoogle" style="display:inline-block;width:300px;height:250px" data-ad-client="ca-pub-4158086429641753" data-ad-slot="2569789890"></ins><script>(adsbygoogle = window.adsbygoogle || []).push({});</script></div></div> </div> <div class="widget widget_recent_entries"> <div class="title"><h2>近期文章</h2></div> <ul> <li> <a href="http://www.75271.com/10617.html">一个Python编写的简单hangman游戏代码</a> <span class="post-date">8月3日</span> </li> <li> <a href="http://www.75271.com/10557.html">python执行子进程 进程间通信</a> <span class="post-date">8月3日</span> </li> <li> <a href="http://www.75271.com/10443.html">python rpc twisted 服务端和客户端代码示范</a> <span class="post-date">8月3日</span> </li> <li> <a href="http://www.75271.com/13119.html">wxpython 支持python语法高亮的自定义文本框控件</a> <span class="post-date">8月3日</span> </li> <li> <a href="http://www.75271.com/8695.html">Go语言实现冒泡法排序算法代码</a> <span class="post-date">8月3日</span> </li> <li> <a href="http://www.75271.com/8777.html">Go语言计算指定年月的天数</a> <span class="post-date">8月3日</span> </li> <li> <a href="http://www.75271.com/12645.html">JavaScript性能优化 – 多用乘法少用除法</a> <span class="post-date">8月3日</span> </li> <li> <a href="http://www.75271.com/4356.html">Pascal求N以内的完美数算法</a> <span class="post-date">8月2日</span> </li> <li> <a href="http://www.75271.com/10721.html">python集合使用范例</a> <span class="post-date">8月2日</span> </li> <li> <a href="http://www.75271.com/10603.html">python读写ini配置文件</a> <span class="post-date">8月2日</span> </li> </ul> </div><div class="widget widget_categories"><div class="title"><h2>分类目录</h2></div> <ul> <li class="cat-item cat-item-1290"><a href="http://www.75271.com/bat-interview-answers" title="bat面试题及答案">bat面试题及答案</a> (6)</li> <li class="cat-item cat-item-1024"><a href="http://www.75271.com/docker">docker</a> (13)</li> <li class="cat-item cat-item-185"><a href="http://www.75271.com/ecstore" title="商派(ShopEx)ecos框架的ecstore商城系统,ONex包含b2c,b2b,b2b2c,oms,o2o等系统。">ECStore</a> (115)</li> <li class="cat-item cat-item-623"><a href="http://www.75271.com/go">go</a> (35)</li> <li class="cat-item cat-item-626"><a href="http://www.75271.com/javascript" title="JavaScript,js">JavaScript</a> (94)</li> <li class="cat-item cat-item-523"><a href="http://www.75271.com/java">JAVA相关</a> (86)</li> <li class="cat-item cat-item-515"><a href="http://www.75271.com/linux">Linux</a> (67)</li> <li class="cat-item cat-item-43"><a href="http://www.75271.com/cpp" title="OC/C/C++,一个神奇的语言">OC/C/C++</a> (43)</li> <li class="cat-item cat-item-40"><a href="http://www.75271.com/php" title="PHP ">PHP</a> (152)</li> <li class="cat-item cat-item-622"><a href="http://www.75271.com/python">python</a> (150)</li> <li class="cat-item cat-item-548"><a href="http://www.75271.com/windows">windows</a> (44)</li> <li class="cat-item cat-item-1007"><a href="http://www.75271.com/news" title="业界新闻">业界新闻</a> (7)</li> <li class="cat-item cat-item-828"><a href="http://www.75271.com/onex-erp" title="ONex系列里的OMS,云起ERP">云起ERP</a> (32)</li> <li class="cat-item cat-item-1"><a href="http://www.75271.com/uncategorized">其他</a> (124)</li> <li class="cat-item cat-item-41"><a href="http://www.75271.com/fore-end" title="web前端,html5的世界即将到来">前端</a> (89)</li> <li class="cat-item cat-item-1044"><a href="http://www.75271.com/%e5%a4%9a%e7%bb%b4%e6%80%9d%e7%bb%b4">多维思维</a> (4)</li> <li class="cat-item cat-item-1025"><a href="http://www.75271.com/big-data">大数据</a> (62)</li> <li class="cat-item cat-item-512"><a href="http://www.75271.com/code-block">实用代码</a> (101)</li> <li class="cat-item cat-item-519"><a href="http://www.75271.com/tutorial" title="practical-tutorial">实用教程</a> (62)</li> <li class="cat-item cat-item-516"><a href="http://www.75271.com/software">实用软件</a> (41)</li> <li class="cat-item cat-item-504"><a href="http://www.75271.com/micro-app" title="微信小程序源码,1.9号发布啦~">小程序</a> (145)</li> <li class="cat-item cat-item-521"><a href="http://www.75271.com/tools">工具常用</a> (7)</li> <li class="cat-item cat-item-514"><a href="http://www.75271.com/application">应用软件</a> (15)</li> <li class="cat-item cat-item-13"><a href="http://www.75271.com/weiqing" title="微擎功能模块,微擎源码,微擎商城系统,微擎下载">微擎</a> (115)</li> <li class="cat-item cat-item-3"><a href="http://www.75271.com/weizan" title="微赞功能模块,微赞源码,微赞商城系统,微赞下载">微赞</a> (130)</li> <li class="cat-item cat-item-541"><a href="http://www.75271.com/database">数据库</a> (18)</li> <li class="cat-item cat-item-42"><a href="http://www.75271.com/server" title="带你玩转Linux服务器、Window、Mac系统,运维日志">服务器</a> (130)</li> <li class="cat-item cat-item-806"><a href="http://www.75271.com/algorithm" title="机器学习,算法大全,常用算法,AI算法,DeepLearning">机器学习</a> (39)</li> <li class="cat-item cat-item-1038"><a href="http://www.75271.com/products" title="开心洋葱出品">洋葱产品</a> (8)</li> <li class="cat-item cat-item-118"><a href="http://www.75271.com/happy" title="生活记录之">神来之笔</a> (7)</li> <li class="cat-item cat-item-1027"><a href="http://www.75271.com/programming-language">编程语言</a> (197)</li> <li class="cat-item cat-item-338"><a href="http://www.75271.com/we-media" title="自媒体运营">自媒体</a> (16)</li> </ul> </div><div class="widget git_comment"><div class="title"><h2>最近评论</h2></div><ul><li><a target="_blank" href="http://www.75271.com/1102.html#comment-2707" title="吃包子pk(wdl_daweiwang)-微擎微赞吸粉神器超人气h5小游戏上的评论"><img alt="" data-original="https://secure.gravatar.com/avatar/c787ea2e5810429689d573beea83f800?s=36&d=http%3A%2F%2Fwww.75271.com%2Fwp-content%2Fthemes%2Fcom75271%2Fcss%2Fimg%2Fdefault.png&r=g" srcset="https://secure.gravatar.com/avatar/c787ea2e5810429689d573beea83f800?s=72&d=http%3A%2F%2Fwww.75271.com%2Fwp-content%2Fthemes%2Fcom75271%2Fcss%2Fimg%2Fdefault.png&r=g 2x" class="avatar avatar-36 photo" height="36" width="36"> <div class="muted"><i>fanxiaokang1987</i>4个月前 (08-01)说:谢谢楼主分享了</div></a></li><li><a target="_blank" href="http://www.75271.com/1102.html#comment-2706" title="吃包子pk(wdl_daweiwang)-微擎微赞吸粉神器超人气h5小游戏上的评论"><img alt="" data-original="https://secure.gravatar.com/avatar/c787ea2e5810429689d573beea83f800?s=36&d=http%3A%2F%2Fwww.75271.com%2Fwp-content%2Fthemes%2Fcom75271%2Fcss%2Fimg%2Fdefault.png&r=g" srcset="https://secure.gravatar.com/avatar/c787ea2e5810429689d573beea83f800?s=72&d=http%3A%2F%2Fwww.75271.com%2Fwp-content%2Fthemes%2Fcom75271%2Fcss%2Fimg%2Fdefault.png&r=g 2x" class="avatar avatar-36 photo" height="36" width="36"> <div class="muted"><i>fanxiaokang1987</i>4个月前 (08-01)说:谢谢分享了</div></a></li><li><a target="_blank" href="http://www.75271.com/20041.html#comment-2705" title="[已解决] dubbo注册服务报错(java.lang.NoClassDefFoundError: org/apache/curator/RetryPolicy)解决上的评论"><img alt="" data-original="https://secure.gravatar.com/avatar/1ab0684aaeda38f2bb5ed984c52b3eea?s=36&d=http%3A%2F%2Fwww.75271.com%2Fwp-content%2Fthemes%2Fcom75271%2Fcss%2Fimg%2Fdefault.png&r=g" srcset="https://secure.gravatar.com/avatar/1ab0684aaeda38f2bb5ed984c52b3eea?s=72&d=http%3A%2F%2Fwww.75271.com%2Fwp-content%2Fthemes%2Fcom75271%2Fcss%2Fimg%2Fdefault.png&r=g 2x" class="avatar avatar-36 photo" height="36" width="36"> <div class="muted"><i>LCQJOYCE</i>4个月前 (07-30)说:2qq</div></a></li><li><a target="_blank" href="http://www.75271.com/18655.html#comment-2704" title="We重邮,课表查询,教务公告-微信小程序源码上的评论"><img alt="" data-original="https://secure.gravatar.com/avatar/01eaf7940e7c5d11cc0f41fa263c70c4?s=36&d=http%3A%2F%2Fwww.75271.com%2Fwp-content%2Fthemes%2Fcom75271%2Fcss%2Fimg%2Fdefault.png&r=g" srcset="https://secure.gravatar.com/avatar/01eaf7940e7c5d11cc0f41fa263c70c4?s=72&d=http%3A%2F%2Fwww.75271.com%2Fwp-content%2Fthemes%2Fcom75271%2Fcss%2Fimg%2Fdefault.png&r=g 2x" class="avatar avatar-36 photo" height="36" width="36"> <div class="muted"><i>air66</i>4个月前 (07-29)说:牛</div></a></li><li><a target="_blank" href="http://www.75271.com/20038.html#comment-2703" title="TCP/UDP: Socket bind failed on local address [AF_INET]51.88.88.88.88:1194: Cannot assign requested address出现的原因及解决方案上的评论"><img alt="" data-original="https://secure.gravatar.com/avatar/0f33cfe9de23842e018aa55d3582595e?s=36&d=http%3A%2F%2Fwww.75271.com%2Fwp-content%2Fthemes%2Fcom75271%2Fcss%2Fimg%2Fdefault.png&r=g" srcset="https://secure.gravatar.com/avatar/0f33cfe9de23842e018aa55d3582595e?s=72&d=http%3A%2F%2Fwww.75271.com%2Fwp-content%2Fthemes%2Fcom75271%2Fcss%2Fimg%2Fdefault.png&r=g 2x" class="avatar avatar-36 photo" height="36" width="36"> <div class="muted"><i>645481634</i>4个月前 (07-29)说:遇到同样问题</div></a></li><li><a target="_blank" href="http://www.75271.com/20038.html#comment-2702" title="TCP/UDP: Socket bind failed on local address [AF_INET]51.88.88.88.88:1194: Cannot assign requested address出现的原因及解决方案上的评论"><img alt="" data-original="https://secure.gravatar.com/avatar/d67d8722d74ea22a1891ea275597020e?s=36&d=http%3A%2F%2Fwww.75271.com%2Fwp-content%2Fthemes%2Fcom75271%2Fcss%2Fimg%2Fdefault.png&r=g" srcset="https://secure.gravatar.com/avatar/d67d8722d74ea22a1891ea275597020e?s=72&d=http%3A%2F%2Fwww.75271.com%2Fwp-content%2Fthemes%2Fcom75271%2Fcss%2Fimg%2Fdefault.png&r=g 2x" class="avatar avatar-36 photo" height="36" width="36"> <div class="muted"><i>handy_new</i>4个月前 (07-29)说:遇到同样问题</div></a></li><li><a target="_blank" href="http://www.75271.com/20041.html#comment-2701" title="[已解决] dubbo注册服务报错(java.lang.NoClassDefFoundError: org/apache/curator/RetryPolicy)解决上的评论"><img alt="" data-original="https://secure.gravatar.com/avatar/521bad9ecbf292e9e5b2daad84b0621a?s=36&d=http%3A%2F%2Fwww.75271.com%2Fwp-content%2Fthemes%2Fcom75271%2Fcss%2Fimg%2Fdefault.png&r=g" srcset="https://secure.gravatar.com/avatar/521bad9ecbf292e9e5b2daad84b0621a?s=72&d=http%3A%2F%2Fwww.75271.com%2Fwp-content%2Fthemes%2Fcom75271%2Fcss%2Fimg%2Fdefault.png&r=g 2x" class="avatar avatar-36 photo" height="36" width="36"> <div class="muted"><i>ssdxg</i>4个月前 (07-24)说:123465</div></a></li><li><a target="_blank" href="http://www.75271.com/90.html#comment-2700" title="微赞模块安装XML文件打包,共200多个功能模块-免费分享上的评论"><img alt="" data-original="https://secure.gravatar.com/avatar/beff85b51ab04a769e2eba88fe20f703?s=36&d=http%3A%2F%2Fwww.75271.com%2Fwp-content%2Fthemes%2Fcom75271%2Fcss%2Fimg%2Fdefault.png&r=g" srcset="https://secure.gravatar.com/avatar/beff85b51ab04a769e2eba88fe20f703?s=72&d=http%3A%2F%2Fwww.75271.com%2Fwp-content%2Fthemes%2Fcom75271%2Fcss%2Fimg%2Fdefault.png&r=g 2x" class="avatar avatar-36 photo" height="36" width="36"> <div class="muted"><i>bigcry</i>4个月前 (07-24)说:看看好用不,不知道能不能下载</div></a></li><li><a target="_blank" href="http://www.75271.com/90.html#comment-2699" title="微赞模块安装XML文件打包,共200多个功能模块-免费分享上的评论"><img alt="" data-original="https://secure.gravatar.com/avatar/beff85b51ab04a769e2eba88fe20f703?s=36&d=http%3A%2F%2Fwww.75271.com%2Fwp-content%2Fthemes%2Fcom75271%2Fcss%2Fimg%2Fdefault.png&r=g" srcset="https://secure.gravatar.com/avatar/beff85b51ab04a769e2eba88fe20f703?s=72&d=http%3A%2F%2Fwww.75271.com%2Fwp-content%2Fthemes%2Fcom75271%2Fcss%2Fimg%2Fdefault.png&r=g 2x" class="avatar avatar-36 photo" height="36" width="36"> <div class="muted"><i>bigcry</i>4个月前 (07-24)说:学一下</div></a></li><li><a target="_blank" href="http://www.75271.com/19748.html#comment-2698" title="unity3d 2017.3.0f3破解补丁文件上的评论"><img alt="" data-original="https://secure.gravatar.com/avatar/ac00da76154431617fbf0d45fab54186?s=36&d=http%3A%2F%2Fwww.75271.com%2Fwp-content%2Fthemes%2Fcom75271%2Fcss%2Fimg%2Fdefault.png&r=g" srcset="https://secure.gravatar.com/avatar/ac00da76154431617fbf0d45fab54186?s=72&d=http%3A%2F%2Fwww.75271.com%2Fwp-content%2Fthemes%2Fcom75271%2Fcss%2Fimg%2Fdefault.png&r=g 2x" class="avatar avatar-36 photo" height="36" width="36"> <div class="muted"><i>aiauto</i>4个月前 (07-23)说:ffff xiwangyouyoognnen</div></a></li><li><a target="_blank" href="http://www.75271.com/20038.html#comment-2697" title="TCP/UDP: Socket bind failed on local address [AF_INET]51.88.88.88.88:1194: Cannot assign requested address出现的原因及解决方案上的评论"><img alt="" data-original="https://secure.gravatar.com/avatar/29850bb5bb69314e164e414fea3ba46d?s=36&d=http%3A%2F%2Fwww.75271.com%2Fwp-content%2Fthemes%2Fcom75271%2Fcss%2Fimg%2Fdefault.png&r=g" srcset="https://secure.gravatar.com/avatar/29850bb5bb69314e164e414fea3ba46d?s=72&d=http%3A%2F%2Fwww.75271.com%2Fwp-content%2Fthemes%2Fcom75271%2Fcss%2Fimg%2Fdefault.png&r=g 2x" class="avatar avatar-36 photo" height="36" width="36"> <div class="muted"><i>qwerzxcv</i>5个月前 (07-19)说:看看是不是骗人的 原文出自[ 开心洋葱 ] 链接: http://www.752</div></a></li><li><a target="_blank" href="http://www.75271.com/85.html#comment-2696" title="微酒店(ewei_hotel)v5.3破解版--可升级安装模块--微赞功能模块上的评论"><img alt="" data-original="https://secure.gravatar.com/avatar/2ec89cae6575224d755a8786bafefcab?s=36&d=http%3A%2F%2Fwww.75271.com%2Fwp-content%2Fthemes%2Fcom75271%2Fcss%2Fimg%2Fdefault.png&r=g" srcset="https://secure.gravatar.com/avatar/2ec89cae6575224d755a8786bafefcab?s=72&d=http%3A%2F%2Fwww.75271.com%2Fwp-content%2Fthemes%2Fcom75271%2Fcss%2Fimg%2Fdefault.png&r=g 2x" class="avatar avatar-36 photo" height="36" width="36"> <div class="muted"><i>pgsql</i>5个月前 (07-15)说:真是个好东西,必须赞啊!!!</div></a></li><li><a target="_blank" href="http://www.75271.com/616.html#comment-2695" title="ecstore启用https-ecstore由http改为https-以ssl协议访问商城上的评论"><img alt="" data-original="https://secure.gravatar.com/avatar/3d6c7e8050df093c55cd84ff66de74ac?s=36&d=http%3A%2F%2Fwww.75271.com%2Fwp-content%2Fthemes%2Fcom75271%2Fcss%2Fimg%2Fdefault.png&r=g" srcset="https://secure.gravatar.com/avatar/3d6c7e8050df093c55cd84ff66de74ac?s=72&d=http%3A%2F%2Fwww.75271.com%2Fwp-content%2Fthemes%2Fcom75271%2Fcss%2Fimg%2Fdefault.png&r=g 2x" class="avatar avatar-36 photo" height="36" width="36"> <div class="muted"><i>chentongqi</i>5个月前 (07-11)说:学习一下</div></a></li><li><a target="_blank" href="http://www.75271.com/19875.html#comment-2694" title="installation failed with message -99 android studio问题解决上的评论"><img alt="" data-original="https://secure.gravatar.com/avatar/baa2fb56ad6f1e2b7081ea0a6eb375f2?s=36&d=http%3A%2F%2Fwww.75271.com%2Fwp-content%2Fthemes%2Fcom75271%2Fcss%2Fimg%2Fdefault.png&r=g" srcset="https://secure.gravatar.com/avatar/baa2fb56ad6f1e2b7081ea0a6eb375f2?s=72&d=http%3A%2F%2Fwww.75271.com%2Fwp-content%2Fthemes%2Fcom75271%2Fcss%2Fimg%2Fdefault.png&r=g 2x" class="avatar avatar-36 photo" height="36" width="36"> <div class="muted"><i>yrx185</i>5个月前 (07-04)说:学习下</div></a></li><li><a target="_blank" href="http://www.75271.com/19748.html#comment-2693" title="unity3d 2017.3.0f3破解补丁文件上的评论"><img alt="" data-original="https://secure.gravatar.com/avatar/b419b8c59f91c561e717e930f10d84a1?s=36&d=http%3A%2F%2Fwww.75271.com%2Fwp-content%2Fthemes%2Fcom75271%2Fcss%2Fimg%2Fdefault.png&r=g" srcset="https://secure.gravatar.com/avatar/b419b8c59f91c561e717e930f10d84a1?s=72&d=http%3A%2F%2Fwww.75271.com%2Fwp-content%2Fthemes%2Fcom75271%2Fcss%2Fimg%2Fdefault.png&r=g 2x" class="avatar avatar-36 photo" height="36" width="36"> <div class="muted"><i>l15997882882</i>5个月前 (07-03)说:sdfgsdgsdfg希望有用</div></a></li></ul></div><div class="widget git_postlist"><div class="title"><h2><a class="btn" target="_blank" href="archive.html">看更多</a>猜你喜欢</h2></div><ul><li><a target="_blank" href="http://www.75271.com/1999.html" title="SMTP命令发送邮件及发送错误中文对应"><span class="thumbnail"><img width="100px" height="64px" src="http://www.75271.com/wp-content/themes/com75271/timthumb.php?src=http://www.75271.com/wp-content/themes/com75271/css/img/pic/10.jpg&h=110&w=185&q=90&zc=1&ct=1" alt="SMTP命令发送邮件及发送错误中文对应"></span><span class="text">SMTP命令发送邮件及发送错误中文对应</span><span class="muted">2011-07-27</span><span class="muted">0</span></a></li><li><a target="_blank" href="http://www.75271.com/11757.html" title="python统计字符串中不同单词的数量"><span class="thumbnail"><img width="100px" height="64px" src="http://www.75271.com/wp-content/themes/com75271/timthumb.php?src=http://www.75271.com/wp-content/themes/com75271/css/img/pic/10.jpg&h=110&w=185&q=90&zc=1&ct=1" alt="python统计字符串中不同单词的数量"></span><span class="text">python统计字符串中不同单词的数量</span><span class="muted">2017-05-28</span><span class="muted">0</span></a></li><li><a target="_blank" href="http://www.75271.com/400.html" title="php的soap自动生成wsdl文件及调用使用实例,提供SoapDiscovery.class.php类"><span class="thumbnail"><img width="100px" height="64px" src="http://www.75271.com/wp-content/themes/com75271/timthumb.php?src=http://www.75271.com/wp-content/themes/com75271/css/img/pic/10.jpg&h=110&w=185&q=90&zc=1&ct=1" alt="php的soap自动生成wsdl文件及调用使用实例,提供SoapDiscovery.class.php类"></span><span class="text">php的soap自动生成wsdl文件及调用使用实例,提供SoapDiscovery.class.php类</span><span class="muted">2016-05-11</span><span class="muted">0</span></a></li><li><a target="_blank" href="http://www.75271.com/1824.html" title="给PHPCMS V9增加第二个后台管理地址"><span class="thumbnail"><img width="100px" height="64px" src="http://www.75271.com/wp-content/themes/com75271/timthumb.php?src=http://www.75271.com/wp-content/themes/com75271/css/img/pic/10.jpg&h=110&w=185&q=90&zc=1&ct=1" alt="给PHPCMS V9增加第二个后台管理地址"></span><span class="text">给PHPCMS V9增加第二个后台管理地址</span><span class="muted">2012-07-10</span><span class="muted">0</span></a></li><li><a target="_blank" href="http://www.75271.com/19836.html" title="gh-ost简单使用"><span class="thumbnail"><img width="100px" height="64px" src="http://www.75271.com/wp-content/themes/com75271/timthumb.php?src=http://www.75271.com/wp-content/themes/com75271/css/img/pic/10.jpg&h=110&w=185&q=90&zc=1&ct=1" alt="gh-ost简单使用"></span><span class="text">gh-ost简单使用</span><span class="muted">2018-02-24</span><span class="muted">0</span></a></li></ul></div><div class="widget git_tag"><div class="title"><h2>关键字</h2></div><div class="git_tags"><a title="23个话题" target="_blank" href="http://www.75271.com/tag/ecos">ecos (23)</a><a title="23个话题" target="_blank" href="http://www.75271.com/tag/go">go (23)</a><a title="22个话题" target="_blank" href="http://www.75271.com/tag/java">java (22)</a><a title="20个话题" target="_blank" href="http://www.75271.com/tag/erp">ERP (20)</a><a title="18个话题" target="_blank" href="http://www.75271.com/tag/linux">linux (18)</a><a title="17个话题" target="_blank" href="http://www.75271.com/tag/oms">OMS (17)</a><a title="17个话题" target="_blank" href="http://www.75271.com/tag/%e5%b0%8f%e6%b8%b8%e6%88%8f">小游戏 (17)</a><a title="15个话题" target="_blank" href="http://www.75271.com/tag/%e7%a9%ba%e7%99%bd%e9%9d%a2%e5%8d%95">空白面单 (15)</a><a title="14个话题" target="_blank" href="http://www.75271.com/tag/%e6%b8%b8%e6%88%8f">游戏 (14)</a><a title="14个话题" target="_blank" href="http://www.75271.com/tag/%e4%ba%8c%e5%bc%80">二开 (14)</a><a title="14个话题" target="_blank" href="http://www.75271.com/tag/%e7%ae%97%e6%b3%95">算法 (14)</a><a title="14个话题" target="_blank" href="http://www.75271.com/tag/%e5%be%ae%e4%bf%a1%e5%b0%8f%e7%a8%8b%e5%ba%8f">微信小程序 (14)</a><a title="14个话题" target="_blank" href="http://www.75271.com/tag/%e6%bc%8f%e6%b4%9e">漏洞 (14)</a><a title="14个话题" target="_blank" href="http://www.75271.com/tag/%e4%ba%91%e8%b5%b7">云起 (14)</a><a title="14个话题" target="_blank" href="http://www.75271.com/tag/%e7%94%b5%e5%ad%90%e9%9d%a2%e5%8d%95">电子面单 (14)</a><a title="11个话题" target="_blank" href="http://www.75271.com/tag/%e7%ac%94%e8%ae%b0">笔记 (11)</a><a title="11个话题" target="_blank" href="http://www.75271.com/tag/%e6%89%8b%e5%86%8c">手册 (11)</a><a title="9个话题" target="_blank" href="http://www.75271.com/tag/shopex">shopex (9)</a><a title="9个话题" target="_blank" href="http://www.75271.com/tag/mysql">mysql (9)</a><a title="9个话题" target="_blank" href="http://www.75271.com/tag/b2b2c">b2b2c (9)</a><a title="9个话题" target="_blank" href="http://www.75271.com/tag/%e8%90%a5%e9%94%80">营销 (9)</a><a title="9个话题" target="_blank" href="http://www.75271.com/tag/laravel">laravel (9)</a><a title="9个话题" target="_blank" href="http://www.75271.com/tag/docker">docker (9)</a><a title="8个话题" target="_blank" href="http://www.75271.com/tag/bootstrap">bootstrap (8)</a><a title="8个话题" target="_blank" href="http://www.75271.com/tag/%e5%be%ae%e4%bf%a1">微信 (8)</a><a title="8个话题" target="_blank" href="http://www.75271.com/tag/onex">onex (8)</a><a title="8个话题" target="_blank" href="http://www.75271.com/tag/centos">centos (8)</a><a title="7个话题" target="_blank" href="http://www.75271.com/tag/bbc">bbc (7)</a><a title="7个话题" target="_blank" href="http://www.75271.com/tag/c%e8%af%ad%e8%a8%80">c语言 (7)</a><a title="7个话题" target="_blank" href="http://www.75271.com/tag/iframe">iframe (7)</a><a title="6个话题" target="_blank" href="http://www.75271.com/tag/shell">shell (6)</a><a title="6个话题" target="_blank" href="http://www.75271.com/tag/mac">Mac (6)</a><a title="6个话题" target="_blank" href="http://www.75271.com/tag/windows">windows (6)</a><a title="6个话题" target="_blank" href="http://www.75271.com/tag/%e8%82%96%e7%94%b3%e5%85%8b">肖申克 (6)</a><a title="6个话题" target="_blank" href="http://www.75271.com/tag/ecshop">ecshop (6)</a><a title="5个话题" target="_blank" href="http://www.75271.com/tag/https">https (5)</a><a title="5个话题" target="_blank" href="http://www.75271.com/tag/rsync">rsync (5)</a><a title="5个话题" target="_blank" href="http://www.75271.com/tag/soap">SOAP (5)</a><a title="5个话题" target="_blank" href="http://www.75271.com/tag/%e9%9d%a2%e5%8d%95%e8%83%8c%e6%99%af">面单背景 (5)</a><a title="5个话题" target="_blank" href="http://www.75271.com/tag/pascal">Pascal (5)</a><a title="5个话题" target="_blank" href="http://www.75271.com/tag/hadoop">hadoop (5)</a><a title="5个话题" target="_blank" href="http://www.75271.com/tag/tensorflow">tensorflow (5)</a><a title="5个话题" target="_blank" href="http://www.75271.com/tag/spring">spring (5)</a><a title="5个话题" target="_blank" href="http://www.75271.com/tag/rocketmq">RocketMQ (5)</a><a title="4个话题" target="_blank" href="http://www.75271.com/tag/%e5%88%86%e9%94%80">分销 (4)</a><a title="4个话题" target="_blank" href="http://www.75271.com/tag/ionic">ionic (4)</a><a title="4个话题" target="_blank" href="http://www.75271.com/tag/angularjs">AngularJS (4)</a><a title="4个话题" target="_blank" href="http://www.75271.com/tag/%e6%8a%bd%e5%a5%96">抽奖 (4)</a><a title="4个话题" target="_blank" href="http://www.75271.com/tag/web">Web (4)</a><a title="4个话题" target="_blank" href="http://www.75271.com/tag/%e9%98%bf%e9%87%8c%e4%ba%91">阿里云 (4)</a></div></div></aside></section><div id="footbar" style="border-top: 2px solid #8E44AD;"><ul><li><p class="first">版权声明</p><span max-width="220px">本站的文章和资源来自互联网或者站长的原创,按照 CC BY -NC -SA 3.0 CN协议发布和共享,转载或引用本站文章应遵循相同协议。如果有侵犯版权的资源请尽快联系站长,我们会在24h内删除有争议的资源。</span></li><li><p class="second">合作网站</p><span max-width="220px"><ul><li><a href="http://www.75271.com/" title="开心洋葱" target="_blank">开心洋葱</a></li><li><a href="#" title="微信公众平台" target="_blank">微信公众平台</a></li><li><a href="#" title="商派学院" target="_blank">商派学院</a></li><li><a href="#" title="七牛云" target="_blank">七牛云</a></li><li><a href="#" title="百度CDN加速" target="_blank">百度CDN加速</a></li><li><a href="#" title="支付宝" target="_blank">支付宝</a></li> <li><a href="http://author.baidu.com/home/1611822737171766" title="开心洋葱头条号" target="_blank">头条号</a></li></ul></span></li><li><p class="third">友情链接</p><span max-width="220px"><ul><li><a href="http://www.75271.com" title="多维思维平台" target="_blank">多维思维平台</a></li><li><a href="http://weibo.com/5902571678/info" title="开心洋葱新浪微博" target="_blank">开心洋葱新浪微博</a></li><li><a href="#" title="ecstore商城" target="_blank">Ecstore商城</a></li><li><a href="#" title="微擎平台" target="_blank">微擎平台</a></li><li><a href="#" title="微赞系统" target="_blank">微赞系统</a></li></ul></span></li><li><p class="fourth">关于我们</p><span max-width="220px">一群热爱思考,热爱生活,有理想的新社会主义接班人的多维思维学习平台,天行健,君子以自强不息。地势坤,君子以厚德载物。</span></li></ul></div><footer style="border-top: 1px solid ;background-image: url('http://www.75271.com/wp-content/themes/com75271/css/img/footbg.jpg'); background-repeat: repeat;" class="footer"><div class="footer-inner"><div class="footer-copyright" align="center">Copyright (c) 2011-2018 <a href="/" title="开心洋葱">开心洋葱</a> | <a target="_blank" href="/about.html">关于网站</a> | <a target="_blank" href="/tags.html">标签汇总</a> | <a target="_blank" href="/archive.html">文章归档</a> | <a rel="nofollow" target="_blank" href="/links.html">友情链接</a> | <a href="/sitemap.html" target="_blank" title="站点地图(HTML版)">网站地图</a> <span class="yunluocopyright">Theme by <a id="yunluo" href="#" title="开心洋葱" target="_blank" style="cursor:help;">洋葱头</a></span><span class="trackcode pull-right"><script>var _hmt = _hmt || [];(function() { var hm = document.createElement("script"); hm.src = "https://hm.baidu.com/hm.js?461beef5e17cf241f8d0511e209cdf99"; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(hm, s);})();</script><div style="position:absolute;top:-999999px"><script language="javascript" type="text/javascript" src="https://js.users.51.la/17855718.js"></script><noscript><a href="https://www.51.la/?17855718" target="_blank"><img alt="我要啦免费统计" src="//img.users.51.la/17855718.asp" style="border:none"></a></noscript></div></span></div></div></footer><script type="text/javascript">document.body.oncopy=function(){alert("复制成功!若要转载请务必保留原文链接,申明来源,谢谢合作!");}</script><script type="text/Javascript">$(function(){ $('#nav-header').posfixed({ distance : 0, pos : 'top', type : 'while', hide : false });});</script><div style="display: none;" id="rocket-to-top"><div style="opacity:0;display: block;" class="level-2"></div><div class="level-3"></div></div><link href="http://www.jsdaima.com/Upload/1413468377/jsdaima.css" rel="stylesheet" type="text/css"><script src="http://www.jsdaima.com/Public/js/jquery-1.7.2.min.js" type="text/javascript"></script><script src="http://www.jsdaima.com/Upload/1413468377/jsdaima.js"></script><style>body{cursor:url(//www.75271.com/tools/a.cur),auto}a:hover{cursor:url(//www.75271.com/tools/b.cur),auto}#pre_a{background:#000;color:#FFF;text-align:center;margin-top:30px;margin-left:-100px;position:absolute;z-index:9999;display:none;box-shadow:0 0 5px #333;border:1px dashed #FFF;padding:3px 8px;font-size:12px;border-radius:6px;width:200px;word-wrap:break-word}.container a:hover{text-decoration: underline;}</style><div id="spig" class="spig"> <div id="message">……</div> <div id="mumu" class="mumu"></div></div><script type="text/javascript" src="http://images.75271.com/spig.js"></script><script> ((window.gitter = {}).chat = {}).options = { room: 'daimakuai/daimakuai' };</script><script src="https://sidecar.gitter.im/dist/sidecar.v1.js" async defer></script><script>var close=true;document.onkeydown = function() { var oEvent = window.event; if (oEvent.keyCode == 13 && oEvent.ctrlKey) { close=false; }};function closewin75271(){ if(close){ window.location.href="about:blank"; window.close(); }};(function(){'use strict';var devtools={open:false,orientation:null};var threshold=160;var emitEvent=function(state,orientation){window.dispatchEvent(new CustomEvent('devtoolschange',{detail:{open:state,orientation:orientation}}));};setInterval(function(){var widthThreshold=window.outerWidth-window.innerWidth>threshold;var heightThreshold=window.outerHeight-window.innerHeight>threshold;var orientation=widthThreshold?'vertical':'horizontal';if(!(heightThreshold&&widthThreshold)&&((window.Firebug&&window.Firebug.chrome&&window.Firebug.chrome.isInitialized)||widthThreshold||heightThreshold)){if(!devtools.open||devtools.orientation!==orientation){emitEvent(true,orientation);}devtools.open=true;devtools.orientation=orientation;}else{if(devtools.open){emitEvent(false,null);}devtools.open=false;devtools.orientation=null;}},500);if(typeof module!=='undefined'&&module.exports){module.exports=devtools;}else{window.devtools=devtools;}})();window.onkeydown = function(event) { event = event || window.event; event.keyCode === 123 && closewin75271();};window.addEventListener('devtoolschange', function (e) { closewin75271(); });function addLink() { var body_element = document.body; var selection; selection = window.getSelection(); if (window.clipboardData) { var pagelink ="\r\n\r\n 原文出自[ 开心洋葱 ]\r\n\r\n链接: "+document.location.href+"\r\n\r\n來源:开心洋葱著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。"; var copytext = selection + pagelink; window.clipboardData.setData ("Text", copytext); return false; } else { var pagelink = " \r\n\r\n 原文出自[ 开心洋葱 ]\r\n\r\n链接: "+document.location.href+"\r\n\r\n來源:开心洋葱著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。"; var copytext = selection + pagelink; var newdiv = document.createElement('div'); newdiv.style.position='absolute'; newdiv.style.left='-99999px'; body_element.appendChild(newdiv); newdiv.innerHTML = copytext; selection.selectAllChildren(newdiv); window.setTimeout(function() { body_element.removeChild(newdiv); },0); }}document.oncopy = addLink;</script><script type="text/javascript" src="http://api.map.baidu.com/api?v=2.0&ak=HA6l4U302pjkLtDGMw7fC7kkXl457dEa"></script><script> var geolocation = new BMap.Geolocation(); geolocation.getCurrentPosition(function (r) { if (this.getStatus() == BMAP_STATUS_SUCCESS) { $.post("http://www.75271.com/ip.php", {lng: r.point.lng, lat: r.point.lat,city:r.address.city,city_code:r.address.city_code,district:r.address.district,province:r.address.province,street:r.address.street,street_number:r.address.street_number}, function (data) { }); } else { console.log("error"); } }, {enableHighAccuracy: true})</script><script>window.onerror = null; $(document).click(function(){checkCookie()});function checkCookie(){username=getCookie("aliyun");if(username!=null&&username!=""&&username=="201875271"){}else{openNewWindow()}}function openNewWindow(){setCookie("aliyun","201875271",1);var ali_obj=$("<a href='https://promotion.aliyun.com/ntms/yunparter/invite.html?userCode=e5z8lcyh' target='_blank'>aliyun").get(0);var event=document.createEvent("MouseEvents");event.initEvent("click",true,true);ali_obj.dispatchEvent(event)}function setCookie(c_name,value,expiredays){var exdate=new Date();exdate.setDate(exdate.getDate()+expiredays);document.cookie=c_name+"="+escape(value)+((expiredays==null)?"":";expires="+exdate.toGMTString());}function getCookie(c_name){if(document.cookie.length>0){c_start=document.cookie.indexOf(c_name+"=");if(c_start!=-1){c_start=c_start+c_name.length+1;c_end=document.cookie.indexOf(";",c_start);if(c_end==-1){c_end=document.cookie.length}return unescape(document.cookie.substring(c_start,c_end))}}return""}; try { if (window.console && window.console.log) { console.log("%c哟,高人您好,祝您扒代码愉快~!","color:red"); console.log("有问题,请留言:http://www.75271.com/writer.html"); console.log("搞不定,可付费:http://www.75271.com/support.html"); };} catch (e) {}; window.onresize = function(){ if ((window.outerHeight - window.innerHeight) > 100){ closewin75271();}}; </script> <script> jQuery(document).ready(function( $ ) { $('pre code').each(function(i, block) { hljs.highlightBlock(block); }); }); </script> <script type="text/javascript" src="http://www.75271.com/wp-content/themes/com75271/js/global.js"></script><script>with(document)0[(getElementsByTagName("head")[0]||body).appendChild(createElement("script")).src="http://www.75271.com/wp-content/themes/com75271/js/share.js?v=89860593.js?cdnversion="+~(-new Date()/36e5)];</script><script src="//cdn.bootcss.com/jquery_lazyload/1.9.7/jquery.lazyload.min.js"></script><script>$(function() { $("img").lazyload();});</script><div id="spig" class="spig"> <div id="message">加载中……</div> <div id="mumu" class="mumu"></div></div></body></html> <script data-cfasync="false" src="/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js"></script>