Thymeleaf模板布局

1. 包含模板片段

定义和引用片段

在我们的模板中,我们经常需要包含其他模板中的部分,页脚,标题,菜单等部分......

为了做到这一点,Thymeleaf需要我们定义这些部分,“片段”,以便包含,这可以使用th:fragment属性来完成。

假设我们要为所有杂货页面添加标准版权页脚,因此我们创建一个/WEB-INF/templates/footer.html包含以下代码的文件:

<!DOCTYPE html>

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

  <body>

    <div th:fragment="copy">
      &copy; 2011 The Good Thymes Virtual Grocery
    </div>

  </body>

</html>

上面的代码定义了一个名为的片段copy,我们可以使用其中一个th:insert或th:replace属性轻松地在我们的主页中包含这些片段(并且th:include,尽管自Thymeleaf 3.0以来不再推荐使用它):

<body>

  ...

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

</body>

请注意,th:insert需要一个片段表达式(~{...}),它是一个导致片段的表达式。在上面的例子中,这是一个非复杂的片段表达式,(~{,})封闭是完全可选的,所以上面的代码相当于:

<body>

  ...

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

</body>

片段规范语法

片段表达式的语法非常简单。有三种不同的格式:

  • "~{templatename::selector}"包括在名为的模板上应用指定标记选择器而产生的片段templatename。请注意,selector可以仅仅是一个片段的名字,所以你可以指定为简单的东西~{templatename::fragmentname}就像在~{footer :: copy}上面。

标记选择器语法由底层AttoParser解析库定义,类似于XPath表达式或CSS选择器。有关详细信息,请参阅附录C.

  • "~{templatename}"包含名为的完整模板templatename。

请注意,您在th:insert/ th:replacetags中使用的模板名称必须由模板引擎当前使用的模板解析器解析。

  • ~{::selector}"或"~{this::selector}"插入来自同一模板的片段,进行匹配selector。如果在表达式出现的模板上找不到,则模板调用(插入)的堆栈将遍历最初处理的模板(根),直到selector在某个级别匹配。

双方templatename并selector在上面的例子可以是全功能的表达式(甚至条件语句!),如:

<div th:insert="footer :: (${user.isAdmin}? #{footer.admin} : #{footer.normaluser})"></div>

再次注意周围的~{...}包络在th:insert/中是如何可选的th:replace。

片段可以包含任何th:* 属性。一旦将片段包含在目标模板(具有th:insert/ th:replaceattribute的模板)中,就会评估这些属性,并且它们将能够引用此目标模板中定义的任何上下文变量。

这种片段方法的一大优点是,您可以在浏览器完美显示的页面中编写片段,具有完整且有效的标记结构,同时仍保留使Thymeleaf将其包含在其他模板中的能力。

引用没有th:fragment的片段

由于标记选择器的强大功能,我们可以包含不使用任何th:fragment属性的片段。它甚至可以是来自不同应用程序的标记代码,完全不了解Thymeleaf:

...
<div id="copy-section">
  &copy; 2011 The Good Thymes Virtual Grocery
</div>
...

我们可以使用上面的片段简单地通过其id属性引用它,类似于CSS选择器:

<body>

  ...

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

</body>

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

和之间有什么区别th:insert和th:replace(和th:include,因为3.0不推荐)?

 

- th:insert 是最简单的:它只是插入指定的片段作为其主机标签的主体。

 

- th:replace实际上用指定的片段替换它的主机标签。

  • th:include类似于th:insert,但不是插入片段,它只插入此片段的内容。

所以像这样的HTML片段:

<footer th:fragment="copy">
  &copy; 2011 The Good Thymes Virtual Grocery
</footer>

...在主机

标签中包含三次,如下所示:

<body>

  ...

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

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

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

</body>

......将导致:

<body>

  ...

  <div>
    <footer>
      &copy; 2011 The Good Thymes Virtual Grocery
    </footer>
  </div>

  <footer>
    &copy; 2011 The Good Thymes Virtual Grocery
  </footer>

  <div>
    &copy; 2011 The Good Thymes Virtual Grocery
  </div>

</body>

 

2. 可参数化的片段签名

为了为模板片段创建更像函数的机制,使用定义的片段th:fragment可以指定一组参数:

<div th:fragment="frag (onevar,twovar)">
    <p th:text="${onevar} + ' - ' + ${twovar}">...</p>
</div>

这需要使用这两种语法之一来从th:insert或调用片段th:replace:

<div th:replace="::frag (${value1},${value2})">...</div>
<div th:replace="::frag (onevar=${value1},twovar=${value2})">...</div>

请注意,顺序在最后一个选项中并不重要:

<div th:replace="::frag (twovar=${value2},onevar=${value1})">...</div>

片段局部变量没有片段参数 即使片段定义没有这样的参数:

<div th:fragment="frag">
    ...
</div>

我们可以使用上面指定的第二种语法来调用它们(只有第二种语法):

<div th:replace="::frag (onevar=${value1},twovar=${value2})">

这将相当于组合th:replace和th:with:

<div th:replace="::frag" th:with="onevar=${value1},twovar=${value2}">

请注意,片段的局部变量规范 - 无论是否具有参数签名 - 都不会导致上下文在执行之前被清空。片段仍然可以像访问目前那样访问调用模板中使用的每个上下文变量。

th:assert for in-template assertions

该th:assert属性可以指定一个以逗号分隔的表达式列表,这些表达式应该被评估并为每次评估生成true,否则会引发异常。

<div th:assert="${onevar},(${twovar} != 43)">...</div>

这对于验证片段签名的参数非常方便:

<header th:fragment="contentheader(title)" th:assert="${!#strings.isEmpty(title)}">...</header>

 

3. 灵活布局:超越单纯的片段插入

由于片段表达式,我们可以为不是文本,数字,bean对象的片段指定参数......而是指定标记片段。

这允许我们以一种方式创建我们的片段,使得它们可以通过来自调用模板的标记来丰富,从而产生非常灵活的模板布局机制。

请注意以下片段中的title和links变量的使用:

<head th:fragment="common_header(title,links)">

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

  <!-- Common styles and scripts -->
  <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>

  <!--/* Per-page placeholder for additional links */-->
  <th:block th:replace="${links}" />

</head>

我们现在可以将这个片段称为:

...
<head th:replace="base :: common_header(~{::title},~{::link})">

  <title>Awesome - Main</title>

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

</head>
...

结果将使用我们的调用模板。

Thymeleaf 将局部变量称为为模板的特定片段定义的变量,并且仅可用于在该片段内进行评估。我们已经看到的一个例子是prod我们的产品列表页面中的iter变量:<tr th:each="prod : $ ...