天天看点

可能是最全的Thymeleaf参考手册(八):模板布局模板片段包含可参数化的片段签名灵活的布局:不仅仅是片段插入布局继承附录:标签选择器语法

模板片段包含

定义和引用片段

在模板中,经常需要包含其他模板中的部分,例如页脚,页眉,菜单等部分。

为了做到这一点,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选择器。有关更多信息,请参见 附录:标签选择器语法
  • "~{templatename}",包括名为 templatename 的完整模板。
请注意,在 th:insert / th:replace 标记中使用的模板名称必须由模板引擎当前正在使用的模板解析器解析。
  • ~{::selector} 或 ~{this::selector},从同一模板插入一个片段,匹配 selector。如果在出现表达式的模板上未找到,则将模板调用(插入)堆栈遍历到原始处理的模板(root),直到 selector 在某个级别上匹配为止。

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

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

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

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

这种片段处理方法的一大优势是,可以将片段写在浏览器可以完美显示的页面中,并具有完整甚至有效的标记结构,同时仍保留使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>
           

th:insert、th:replace、th:include之间的差异

th:insert、th:replace、th:include(3.0之后不推荐使用)之间有何区别?

  • th:insert 最简单,它简单地将指定的片段作为其host标签的主体插入。
  • th:replace 实际上将其标签替换为指定的片段。
  • th:include 与 th:insert 相似 ,但不插入片段,而是仅插入该片段的内容。

HTML片段如下所示:

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

分别在 <div> 标签中包含3次,如下所示:

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

可参数化的片段签名

为了为模板片段创建更类似于函数的机制,使用 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模板内断言

th:assert 属性可以指定一个逗号分隔的表达式列表,应对其进行评估且每次评估都应为 true,否则将引发异常。

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

这对于验证片段签名中的参数非常有用:

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

灵活的布局:不仅仅是片段插入

借助片段表达式,我们可以为片段指定参数,这些参数不是文本,数字,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}" target="_blank" rel="external nofollow" >
  <link rel="shortcut icon" th:href="@{/images/favicon.ico}" target="_blank" rel="external nofollow" >
  <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}" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >
  <link rel="stylesheet" th:href="@{/themes/smoothness/jquery-ui.css}" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >
​
</head>
...
           

结果将使用调用模板中的实际 <title> 和 <link> 标记作为 title 和 links 变量的值,从而导致我们的片段在插入过程中被自定义:

...
<head>
​
  <title>Awesome - Main</title>
​
  <!-- Common styles and scripts -->
  <link rel="stylesheet" type="text/css" media="all" href="/awe/css/awesomeapp.css" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >
  <link rel="shortcut icon" href="/awe/images/favicon.ico" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >
  <script type="text/javascript" src="/awe/sh/scripts/codebase.js"></script>
​
  <link rel="stylesheet" href="/awe/css/bootstrap.min.css" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >
  <link rel="stylesheet" href="/awe/themes/smoothness/jquery-ui.css" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >
​
</head>
...
           

使用空片段

一个特殊的片段表达式,空片段(~{}),可用于指定无标记。使用前面的示例:

<head th:replace="base :: common_header(~{::title},~{})">
​
  <title>Awesome - Main</title>
​
</head>
...
           

请注意片段(links)的第二个参数如何设置为空片段,因此没有为 <th:block th:replace="${links}" /> 块编写任何内容:

...
<head>
​
  <title>Awesome - Main</title>
​
  <!-- Common styles and scripts -->
  <link rel="stylesheet" type="text/css" media="all" href="/awe/css/awesomeapp.css" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >
  <link rel="shortcut icon" href="/awe/images/favicon.ico" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >
  <script type="text/javascript" src="/awe/sh/scripts/codebase.js"></script>
​
</head>
...
           

使用无操作令牌

如果我们只想让我们的片段使用其当前标记作为默认值,那么no-op也可以用作片段的参数。再次使用common_header 示例:

...
<head th:replace="base :: common_header(_,~{::link})">
​
  <title>Awesome - Main</title>
​
  <link rel="stylesheet" th:href="@{/css/bootstrap.min.css}" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >
  <link rel="stylesheet" th:href="@{/themes/smoothness/jquery-ui.css}" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >
​
</head>
...
           

查看 title 参数(common_header 片段的第一个参数)如何设置为no-op(_),这将导致片段的这一部分根本不执行(title= no-operation):

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

结果为:

...
<head>
​
  <title>The awesome application</title>
​
  <!-- Common styles and scripts -->
  <link rel="stylesheet" type="text/css" media="all" href="/awe/css/awesomeapp.css" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >
  <link rel="shortcut icon" href="/awe/images/favicon.ico" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >
  <script type="text/javascript" src="/awe/sh/scripts/codebase.js"></script>
​
  <link rel="stylesheet" href="/awe/css/bootstrap.min.css" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >
  <link rel="stylesheet" href="/awe/themes/smoothness/jquery-ui.css" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >
​
</head>
...
           

片段的高级条件插入

空片段和无操作令牌的可用性使我们能够以非常容易且优雅的方式有条件地插入片段。

例如,我们可以做到这一点,只有当用户是管理员才插入 common :: adminhead 片段,如果不是则不插入任何内容(空片段):

...
<div th:insert="${user.isAdmin()} ? ~{common :: adminhead} : ~{}">...</div>
...
           

同样,我们可以使用no-operation标记来在仅满足指定条件时插入片段,而在不满足条件的情况下不做任何修改就保留标记:

...
<div th:insert="${user.isAdmin()} ? ~{common :: adminhead} : _">
    Welcome [[${user.name}]], click <a th:href="@{/support}" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >here</a> for help-desk support.
</div>
...
           

另外,如果我们已经配置了模板解析器以通过其标志 - 检查模板资源 checkExistence 的存在,我们可以使用片段本身的存在作为默认操作中的条件:

...
<!-- The body of the <div> will be used if the "common :: salutation" fragment  -->
<!-- does not exist (or is empty).                                              -->
<div th:insert="~{common :: salutation} ?: _">
    Welcome [[${user.name}]], click <a th:href="@{/support}" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >here</a> for help-desk support.
</div>
...
           

删除模板片段

看一段示例应用程序:

<table>
  <tr>
    <th>NAME</th>
    <th>PRICE</th>
    <th>IN STOCK</th>
    <th>COMMENTS</th>
  </tr>
  <tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'">
    <td th:text="${prod.name}">Onions</td>
    <td th:text="${prod.price}">2.41</td>
    <td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
    <td>
      <span th:text="${#lists.size(prod.comments)}">2</span> comment/s
      <a href="comments.html" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  
         th:href="@{/product/comments(prodId=${prod.id})}" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  
         th:unless="${#lists.isEmpty(prod.comments)}">view</a>
    </td>
  </tr>
</table>
           

这段代码作为模板很好用,但是作为静态页面(由浏览器直接打开而不由Thymeleaf处理)将不能成为一个好的原型。

为什么?因为尽管该表格可被浏览器完美显示,但该表仅有一行,并且该行仅有模拟数据。作为原型,它看起来根本不够现实……我们应该有多个产品,我们需要更多行。

因此,我们添加一些:

<table>
  <tr>
    <th>NAME</th>
    <th>PRICE</th>
    <th>IN STOCK</th>
    <th>COMMENTS</th>
  </tr>
  <tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'">
    <td th:text="${prod.name}">Onions</td>
    <td th:text="${prod.price}">2.41</td>
    <td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
    <td>
      <span th:text="${#lists.size(prod.comments)}">2</span> comment/s
      <a href="comments.html" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  
         th:href="@{/product/comments(prodId=${prod.id})}" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  
         th:unless="${#lists.isEmpty(prod.comments)}">view</a>
    </td>
  </tr>
  <tr class="odd">
    <td>Blue Lettuce</td>
    <td>9.55</td>
    <td>no</td>
    <td>
      <span>0</span> comment/s
    </td>
  </tr>
  <tr>
    <td>Mild Cinnamon</td>
    <td>1.99</td>
    <td>yes</td>
    <td>
      <span>3</span> comment/s
      <a href="comments.html" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >view</a>
    </td>
  </tr>
</table>
           

好的,现在我们有三个,对于原型来说绝对更好。但是……当我们用Thymeleaf处理它时会发生什么?:

<table>
  <tr>
    <th>NAME</th>
    <th>PRICE</th>
    <th>IN STOCK</th>
    <th>COMMENTS</th>
  </tr>
  <tr>
    <td>Fresh Sweet Basil</td>
    <td>4.99</td>
    <td>yes</td>
    <td>
      <span>0</span> comment/s
    </td>
  </tr>
  <tr class="odd">
    <td>Italian Tomato</td>
    <td>1.25</td>
    <td>no</td>
    <td>
      <span>2</span> comment/s
      <a href="/gtvg/product/comments?prodId=2" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >view</a>
    </td>
  </tr>
  <tr>
    <td>Yellow Bell Pepper</td>
    <td>2.50</td>
    <td>yes</td>
    <td>
      <span>0</span> comment/s
    </td>
  </tr>
  <tr class="odd">
    <td>Old Cheddar</td>
    <td>18.75</td>
    <td>yes</td>
    <td>
      <span>1</span> comment/s
      <a href="/gtvg/product/comments?prodId=4" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >view</a>
    </td>
  </tr>
  <tr class="odd">
    <td>Blue Lettuce</td>
    <td>9.55</td>
    <td>no</td>
    <td>
      <span>0</span> comment/s
    </td>
  </tr>
  <tr>
    <td>Mild Cinnamon</td>
    <td>1.99</td>
    <td>yes</td>
    <td>
      <span>3</span> comment/s
      <a href="comments.html" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >view</a>
    </td>
  </tr>
</table>
           

最后两行是模拟行!好吧,原因当然是:迭代仅应用于第一行,而Thymeleaf也没有理由删除其它两行。

我们需要一种在模板处理期间删除这两行的方法。让我们 th:remove 在第二个和第三个 <tr> 标记上使用该属性:

<table>
  <tr>
    <th>NAME</th>
    <th>PRICE</th>
    <th>IN STOCK</th>
    <th>COMMENTS</th>
  </tr>
  <tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'">
    <td th:text="${prod.name}">Onions</td>
    <td th:text="${prod.price}">2.41</td>
    <td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
    <td>
      <span th:text="${#lists.size(prod.comments)}">2</span> comment/s
      <a href="comments.html" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  
         th:href="@{/product/comments(prodId=${prod.id})}" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  
         th:unless="${#lists.isEmpty(prod.comments)}">view</a>
    </td>
  </tr>
  <tr class="odd" th:remove="all">
    <td>Blue Lettuce</td>
    <td>9.55</td>
    <td>no</td>
    <td>
      <span>0</span> comment/s
    </td>
  </tr>
  <tr th:remove="all">
    <td>Mild Cinnamon</td>
    <td>1.99</td>
    <td>yes</td>
    <td>
      <span>3</span> comment/s
      <a href="comments.html" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >view</a>
    </td>
  </tr>
</table>
           

处理后,内容将重新显示为:

<table>
  <tr>
    <th>NAME</th>
    <th>PRICE</th>
    <th>IN STOCK</th>
    <th>COMMENTS</th>
  </tr>
  <tr>
    <td>Fresh Sweet Basil</td>
    <td>4.99</td>
    <td>yes</td>
    <td>
      <span>0</span> comment/s
    </td>
  </tr>
  <tr class="odd">
    <td>Italian Tomato</td>
    <td>1.25</td>
    <td>no</td>
    <td>
      <span>2</span> comment/s
      <a href="/gtvg/product/comments?prodId=2" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >view</a>
    </td>
  </tr>
  <tr>
    <td>Yellow Bell Pepper</td>
    <td>2.50</td>
    <td>yes</td>
    <td>
      <span>0</span> comment/s
    </td>
  </tr>
  <tr class="odd">
    <td>Old Cheddar</td>
    <td>18.75</td>
    <td>yes</td>
    <td>
      <span>1</span> comment/s
      <a href="/gtvg/product/comments?prodId=4" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >view</a>
    </td>
  </tr>
</table>
           

all 该属性中的值是什么意思?th:remove 可以根据其值以五种不同的方式表现:

  • all:删除包含标签及其所有子标签。
  • body:请勿删除包含标签,而是删除其所有子标签。
  • tag:删除包含的标签,但不要删除其子级。
  • all-but-first:除去第一个标签以外的所有包含标签的子标签。
  • none:什么也不做。该值对于动态评估很有用。

all-but-first 值有什么用?这将使我们 th:remove="all" 在制作原型时节省一些:

<table>
  <thead>
    <tr>
      <th>NAME</th>
      <th>PRICE</th>
      <th>IN STOCK</th>
      <th>COMMENTS</th>
    </tr>
  </thead>
  <tbody th:remove="all-but-first">
    <tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'">
      <td th:text="${prod.name}">Onions</td>
      <td th:text="${prod.price}">2.41</td>
      <td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
      <td>
        <span th:text="${#lists.size(prod.comments)}">2</span> comment/s
        <a href="comments.html" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  
           th:href="@{/product/comments(prodId=${prod.id})}" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  
           th:unless="${#lists.isEmpty(prod.comments)}">view</a>
      </td>
    </tr>
    <tr class="odd">
      <td>Blue Lettuce</td>
      <td>9.55</td>
      <td>no</td>
      <td>
        <span>0</span> comment/s
      </td>
    </tr>
    <tr>
      <td>Mild Cinnamon</td>
      <td>1.99</td>
      <td>yes</td>
      <td>
        <span>3</span> comment/s
        <a href="comments.html" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >view</a>
      </td>
    </tr>
  </tbody>
</table>
           

th:remove 属性可采取任何Thymeleaf标准表示,只要它返回所允许的字符串值中的一个(all,tag,body,all-but-first 或 none)。

这意味着删除可能是有条件的,例如:

<a href="/something" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  th:remove="${condition}? tag : none">Link text not to be removed</a>
           

还要注意,th:remove 考虑 null 的同义词 none,因此以下内容与上面的示例相同:

<a href="/something" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  th:remove="${condition}? tag">Link text not to be removed</a>
           

在这种情况下,如果 ${condition} 为 false,null 将被返回,因此不会执行删除。

布局继承

为了能够将单个文件作为布局,可以使用片段。具有 title 和 content 使用 th:fragment 和的简单布局的示例th:replace:

<!DOCTYPE html>
<html th:fragment="layout (title, content)" xmlns:th="http://www.thymeleaf.org">
<head>
    <title th:replace="${title}">Layout Title</title>
</head>
<body>
    <h1>Layout H1</h1>
    <div th:replace="${content}">
        <p>Layout content</p>
    </div>
    <footer>
        Layout footer
    </footer>
</body>
</html>
           

此示例声明一个名为 layout 的片段,其中标题和内容为参数。在下面的示例中,这两者都将在页面上被继承的片段表达式替换,并继承它。

<!DOCTYPE html>
<html th:replace="~{layoutFile :: layout(~{::title}, ~{::section})}">
<head>
    <title>Page Title</title>
</head>
<body>
<section>
    <p>Page content</p>
    <div>Included on page</div>
</section>
</body>
</html>
           

在这个文件中,该 html 标签将被替换的布局,但在布局 title 和 content 将已被替换 title,并 section 分别块。

如果需要,布局可以由几个片段组成,例如 header 和 footer。

附录:标签选择器语法

Thymeleaf的标记选择器直接从Thymeleaf的解析库:AttoParser中借用。

此选择器的语法与XPath,CSS和jQuery中的选择器具有很大的相似性,这使得它们易于用于大多数用户。

例如,以下选择器将在标记内的每个位置选择每个class为 content 的 <div>(注意这不是尽可能简洁,请继续阅读以了解原因):

<div th:insert="mytemplate :: //div[@class='content']">...</div>
           

基本语法包括:

  • /x 表示名称为x的当前节点的直接子节点。
  • //x 表示任何深度的名称为x的当前节点的子节点。
  • x[@z="v"] 表示名称为x的元素和名为z的属性,值为“v”。
  • x[@z1="v1" and @z2="v2"] 表示名称为x的元素,属性z1和z2分别为值“v1”和“v2”。
  • x[i] 表示名称为x的元素,位于其兄弟姐妹中的数字i中。
  • x[@z="v"][i] 表示名称为x的元素,属性z的值为“v”,并且在其兄弟姐妹中的数字i中也与此条件匹配。

但也可以使用更简洁的语法:

  •  x 完全等价于 //x(x 在任何深度级别搜索具有名称或引用的元素,引用是属性 th:ref 或 th:fragment 属性)。
  •  选择器也允许没有元素名称/引用,只要它们包含参数规范。因此 [@class='oneclass'] 是一个有效的选择器,它使用带有值的class属性查找任何元素(标记)"oneclass"。

高级属性选择功能:

  • 除了 =(相等)之外,其他比较运算符也是有效的:( != 不等于),^=(以...开头)和 $=(以...结尾)。例如:x[@class^='section'] 表示具有名称的元素 x 和以 ... class 开头的属性值 section。
  • 可以从 @(XPath样式)和不带(jQuery样式)开始指定属性。所以 x[z='v'] 相当于 x[@z='v']。
  • 多属性修饰符既可以与 and(XPath样式)连接,也可以通过链接多个修饰符(jQuery样式)来连接。所以x[@z1='v1' and @z2='v2'] 实际上相当于x[@z1='v1'][@z2='v2'](也是 x[z1='v1'][z2='v2'])。

类似jQuery的直接选择器:

  • x.oneclass 相当于 x[class='oneclass']。
  • .oneclass 相当于 [class='oneclass']。
  • x#oneid 相当于 x[id='oneid']。
  • #oneid 相当于 [id='oneid']。
  • x%oneref 表示 <x> 具有 th:ref="oneref" 或 th:fragment="oneref" 属性的标记。
  • %oneref 表示具有 th:ref="oneref" 或 th:fragment="oneref" 属性的任何标记。请注意,这实际上等同于简单的oneref,因为可以使用引用而不是元素名称。
  • 直接选择器和属性选择器可以混合使用:a.external[@href^='https']。

所以上面的Markup Selector表达式:

<div th:insert="mytemplate :: //div[@class='content']">...</div>
           

可以写为:

<div th:insert="mytemplate :: div.content">...</div>
           

另外一个不同的例子:

<div th:replace="mytemplate :: myfrag">...</div>
           

将寻找 th:fragment="myfrag" 片段签名(或 th:ref 引用)。但是myfrag如果它们存在的话,它们也会查找带有名称的标签(它们不是HTML格式的标签)。注意区别:

<div th:replace="mytemplate :: .myfrag">...</div>
           

实际上会查找任何元素 class="myfrag",而不关心 th:fragment 签名(或 th:ref 引用)。

多class匹配

标记选择器将类属性理解为多值,因此即使元素具有多个类值,也允许在此属性上应用选择器。

例如,div.two 将匹配 <div class="one two three" />。

可能是最全的Thymeleaf参考手册(八):模板布局模板片段包含可参数化的片段签名灵活的布局:不仅仅是片段插入布局继承附录:标签选择器语法

                          公众号回复以下关键字,获取更多资源

SpringCloud进阶之路 | Java 基础 | 微服务 | JAVA WEB | JAVA 进阶 | JAVA 面试 | MK 精讲

可能是最全的Thymeleaf参考手册(八):模板布局模板片段包含可参数化的片段签名灵活的布局:不仅仅是片段插入布局继承附录:标签选择器语法

笔者开通了个人微信公众号【银河架构师】,分享工作、生活过程中的心得体会,填坑指南,技术感悟等内容,会比博客提前更新,欢迎订阅。

可能是最全的Thymeleaf参考手册(八):模板布局模板片段包含可参数化的片段签名灵活的布局:不仅仅是片段插入布局继承附录:标签选择器语法