30.SpringBoot学习笔记

Thymeleaf语法

Posted by Chen Xingxu on June 20, 2020

30.SpringBoot学习笔记–Thymeleaf语法

Thymeleaf 自动渲染

只要把 HTML 页面放在 classpath:/templates/,Thymeleaf 就能自动渲染。

参考 org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties

@ConfigurationProperties(prefix = "spring.thymeleaf")
public class ThymeleafProperties {

	private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;

	public static final String DEFAULT_PREFIX = "classpath:/templates/";

	public static final String DEFAULT_SUFFIX = ".html";
}

示例

HelloController

package demo.yangxu.springboot.controller;

@Controller
public class HelloController {
    @RequestMapping("/success")
    public String success(){
        //classpath:/templates/success.html
        return "success";
    }
}

创建 success.html,路径为:src\main\resources\templates\success.html

在浏览器中访问:

http://localhost:8080/success

Thymeleaf 使用方法

官方文档:

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html

示例

查出一些数据,在页面展示。

HelloController

package demo.yangxu.springboot.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.Map;

@Controller
public class HelloController {
    @RequestMapping("/success")
    public String success(Map<String,Object> map){
        map.put("hello","你好");
        return "success";
    }
}

src\main\resources\templates\success.html

1、导入 Thymeleaf 的名称空间

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

参考:using-texts

2、使用 Thymeleaf 语法

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>success!</h1>
    <!--th:text 设置div里面的文本内容 -->
    <div th:text="${hello}">这是显示欢迎信息</div>
</body>
</html>

3、访问

http://localhost:8080/success

4、访问结果

Thymeleaf 语法规则

th:text

改变当前元素内的文本内容

th

对于任意 html 属性,替换原生属性的值

例如:

<div id="div01" class="myDiv" th:id="${hello}" th:class="${hello}" th:text="${hello}">这是显示欢迎信息</div>

启动项目后,在浏览器中访问,查看网页的源代码:

<div id="你好" class="你好">你好</div>

div 中的 id 和 class 属性均被替换。

官网给出的所有 th 类 attribute:

Order Feature Attributes
1 Fragment inclusion th:insert th:replace
2 Fragment iteration th:each
3 Conditional evaluation th:if th:unless th:switch th:case
4 Local variable definition th:object th:with
5 General attribute modification th:attr th:attrprepend th:attrappend
6 Specific attribute modification th:value th:href th:src ...
7 Text (tag body modification) th:text th:utext
8 Fragment specification th:fragment
9 Fragment removal th:remove

参考:attribute-precedence

与 JSP 类比:

Order Feature Attributes
1 片段包含 jsp:include th:insert th:replace
2 遍历 c:forEach th:each
3 条件判断 c:if th:if th:unless th:switch th:case
4 声明变量 c:set th:object th:with
5 任意属性修改 支持prepend,append th:attr th:attrprepend th:attrappend
6 修改指定属性默认值 th:value th:href th:src ...
7 修改标签体内容 th:text(转义特殊字符) th:utext(不转义特殊字符)
8 声明片段 th:fragment
9 移除片段 th:remove

表达式

官网给出的表达式语法:

  • 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: _

参考:standard-expression-syntax

Variable Expressions: ${...}

获取变量值,可以使用 OGNL。

官网给出的 OGNL 示例:

/*
 * 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('-')}

参考:variables

高级特性:

1、获取对象的属性、调用方法;

2、使用内置的基本对象:

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

参考:Expression Basic Objects

参考:Appendix A: Expression Basic Objects

3、使用内置的工具对象:

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

参考:Expression Utility Objects

参考:Appendix B: Expression Utility Objects

Selection Variable Expressions: *{...}

变量的选择表达式,和 ${} 在功能上是一样。与 ${} 不同在于,可以配合 th:object=”${session.user} 使用。

官网给出的说明和示例:

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}">Sebastian</span>.</p>
    <p>Surname: <span th:text="*{lastName}">Pepper</span>.</p>
    <p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
</div>

Which is exactly equivalent to:

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

Of course, dollar and asterisk syntax can be mixed:

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

When 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}">Sebastian</span>.</p>
  <p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p>
  <p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
</div>

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

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

参考:Expressions on selections (asterisk syntax)

Message Expressions: #{...}

获取国际化内容。

参考:Messages

定义 URL。

官网给出的示例:

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

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

<!-- Will produce '/gtvg/order/3/details' (plus rewriting) -->
<a href="details.html" th:href="@{/order/{orderId}/details(orderId=${o.id})}">view</a>

If several parameters are needed, these will be separated by commas: @{/order/process(execId=${execId},execType='FAST')}

参考:Link URLs

Fragment Expressions: ~{...}

片段引用表达式。

官网给出的示例:

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

参考:Fragments

Iteration

官网给出的示例:

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:

<!DOCTYPE html>

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

  <head>
    <title>Good Thymes Virtual Grocery</title>
    <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 list</h1>
  
    <table>
      <tr>
        <th>NAME</th>
        <th>PRICE</th>
        <th>IN STOCK</th>
      </tr>
      <tr th:each="prod : ${prods}">
        <td th:text="${prod.name}">Onions</td>
        <td th:text="${prod.price}">2.41</td>
        <td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
      </tr>
    </table>
  
    <p>
      <a href="../home.html" th:href="@{/}">Return to home</a>
    </p>

  </body>

</html>

参考:Iteration

Inlining

官网给出的示例:

Although the Standard Dialect allows us to do almost everything using tag attributes, there are situations in which we could prefer writing expressions directly into our HTML texts. For example, we could prefer writing this:

<p>Hello, [[${session.user.name}]]!</p>

Expressions between [[...]] or [(...)] are considered inlined expressions in Thymeleaf, and inside them we can use any kind of expression that would also be valid in a th:text or th:utext attribute.

Note that, while [[...]] corresponds to th:text (i.e. result will be HTML-escaped), [(...)] corresponds to th:utext and will not perform any HTML-escaping.

参考:Inlining

示例

HelloController

package demo.yangxu.springboot.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.Arrays;
import java.util.Map;

@Controller
public class HelloController {
    @RequestMapping("/success")
    public String success(Map<String,Object> map){
        map.put("hello","<h1>你好</h1>");
        map.put("users", Arrays.asList("zhangsan","lisi","wangwu"));
        return "success";
    }
}

templates\success.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>成功!</h1>
    <!--th:text 设置div里面的文本内容 -->
    <div id="div01" class="myDiv" th:id="${hello}" th:class="${hello}" th:text="${hello}">这是显示欢迎信息</div>
    <hr/>
    <div th:text="${hello}"></div>
    <div th:utext="${hello}"></div>
    <hr/>
    <!-- th:each每次遍历都会生成当前这个标签: 3个h4 -->
    <h4 th:text="${user}" th:each="user:${users}"></h4>
    <hr/>
    <h4>
        <span th:each="user:${users}">[[${user}]]</span>
    </h4>
</body>
</html>

运行效果:

运行后的网页源码:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>成功!</h1>
    <!--th:text 设置div里面的文本内容 -->
    <div id="&lt;h1&gt;你好&lt;/h1&gt;" class="&lt;h1&gt;你好&lt;/h1&gt;">&lt;h1&gt;你好&lt;/h1&gt;</div>
    <hr/>
    <div>&lt;h1&gt;你好&lt;/h1&gt;</div>
    <div><h1>你好</h1></div>
    <hr/>
    <!-- th:each每次遍历都会生成当前这个标签: 3个h4 -->
    <h4>zhangsan</h4>
    <h4>lisi</h4>
    <h4>wangwu</h4>
    <hr/>
    <h4>
        <span>zhangsan</span><span>lisi</span><span>wangwu</span>
    </h4>
</body>
</html>