Thymeleaf 变量

1、简介

本文将通过一个 Spring Boot 示例带你了解 Thymeleaf 中的变量。

2、Maven 依赖

要使用 Thymeleaf,需要添加 spring-boot-starter-thymeleafspring-boot-starter-web 依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

3、Web Controller

首先,创建一个带有 GET 端点的 Web Controller,该端点返回一个包含文章列表的页面。

@GetMapping 方法只接受一个参数 - Model。它包含所有可在 Thymeleaf 模板中使用的全局变量。在本例中,Model 只有一个参数,即文章列表。

Article 类由两个 String 字段(nameurl)组成:

public class Article {
    private String name;
    private String url;

    // 构造函数、get、set 方法省略
}

Controller 方法的返回值应该是要渲染的 Thymeleaf 模板的名称。该名称应与 src/resource/template 目录中的 HTML 文件相对应。在本例中,就是 src/resource/template/articles-list.html

Controller 如下:

@Controller
@RequestMapping("/api/articles")
public class ArticlesController {

    @GetMapping
    public String allArticles(Model model) {
        model.addAttribute("articles", fetchArticles());
        return "articles-list";
    }

    private List<Article> fetchArticles() {
        return Arrays.asList(
          new Article(
            "Spring Boot 中文文档",
            "https://springdoc.cn/spring-boot/"
          ),
          // 其他一些文章
        );
    }
}

运行应用后,访问 http://localhost:8080/articles 以查看文章页面。

4、Thymeleaf 模板

它具有标准的 HTML 文档结构,只是多了 Thymeleaf 命名空间定义:

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

在后续的示例中,我们将以此为模板,只替换 <main> 标签的内容:

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Thymeleaf Variables</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
    <main>
        ...
    </main>
</body>
</html>

5、定义变量

在 Thymeleaf 模板中定义变量有两种方法。第一种方法是在遍历数组时使用单个元素:

<div th:each="article : ${articles}">
    <a th:text="${article.name}" th:href="${article.url}"></a>
</div>

渲染结果是一个 <div>,其中包含多个 <a> 元素,与 articles 变量中的文章数量相对应。

另一种方法是根据另一个变量定义一个新变量。例如,可以取 articles 数组的第一个元素:

<div th:with="firstArticle=${articles[0]}">
    <a th:text="${firstArticle.name}" th:href="${firstArticle.url}"></a>
</div>

或者,也可以创建一个新变量,只保存文章的名称:

<div th:each="article : ${articles}", th:with="articleName=${article.name}">
    <a th:text="${articleName}" th:href="${article.url}"></a>
</div>

在上例中,${article.name}${articleName} 渲染结果一样。

还可以定义多个变量。例如,可以创建两个单独的变量来保存文章名称和 URL:

<div th:each="article : ${articles}" th:with="articleName=${article.name}, articleUrl=${article.url}">
    <a th:text="${articleName}" th:href="${articleUrl}"></a>
</div>

6、变量范围

Controller 中传递给 Model 的变量具有全局范围(Global Scope)。这意味着它们可以在 HTML 模板的任何地方使用。

而在 HTML 模板中定义的变量具有局部范围。它们只能在其定义的元素范围内使用。

例如,下面的代码是正确的,因为 <a> 元素位于 firstDiv 中:

<div id="firstDiv" th:with="firstArticle=${articles[0]}">
    <a th:text="${firstArticle.name}" th:href="${firstArticle.url}"></a>
</div>

而,尝试在另一个 div 中使用 firstArticle 时:

<div id="firstDiv" th:with="firstArticle=${articles[0]}">
    <a th:text="${firstArticle.name}" th:href="${firstArticle.url}"></a>
</div>
<div id="secondDiv">
    <h2 th:text="${firstArticle.name}"></h2>
</div>

这会在模板编译时抛出异常,提示 firstArticlenull

org.springframework.expression.spel.SpelEvaluationException: EL1007E: Property or field 'name' cannot be found on null

这是因为 <h2> 元素试图使用在 firstDiv 中定义的变量,而该变量已超出范围

如果仍然需要在 secondDiv 中使用 firstArticle 变量,就需要在 secondDiv 中再次定义它,或者将这两个 div 标签包在一个共同的元素中,并在其中定义 firstArticle

7、修改变量值

也可以在给定的作用域中覆盖变量的值:

<div id="mainDiv" th:with="articles = ${ { articles[0], articles[1] } }">
    <div th:each="article : ${articles}">
        <a th:text="${article.name}" th:href="${article.url}"></a>
    </div>
</div>

如上,重新定义了 articles 变量,使其只有前 2 个元素。

注意,在 mainDiv 之外,articles 变量仍将保留其在 Controller 中传递的原始值。

8、总结

本文介绍了如何在 Thymeleaf 中定义和使用变量,还介绍了变量的范围以及如何修改变量。


Ref:https://www.baeldung.com/thymeleaf-variables