1. 概述
在本教程中,我们将展示如何使用Thymeleaf Fragments来重用站点的一些公共部分。在建立一个非常简单的Spring MVC项目之后,我们将关注视图。
如果你是Thymeleaf的新手,你可以查看本网站上的其他文章,例如这篇介绍,以及这篇关于3.0版本引擎的文章。
2. Maven依赖
我们需要一些依赖项来启用Thymeleaf:
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.11.RELEASE</version>
</dependency>
可以在Maven Central上找到最新版本的thymeleaf和thymeleaf-spring5。
3. Spring项目
3.1 Spring MVC配置
要启用Thymeleaf并设置模板后缀,我们需要使用视图解析器和模板解析器配置MVC。
我们还将为一些静态资源设置目录:
@Bean
public ViewResolver htmlViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine(htmlTemplateResolver()));
resolver.setContentType("text/html");
resolver.setCharacterEncoding("UTF-8");
resolver.setViewNames(ArrayUtil.array("*.html"));
return resolver;
}
private ITemplateResolver htmlTemplateResolver() {
SpringResourceTemplateResolver resolver
= new SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
resolver.setPrefix("/WEB-INF/views/");
resolver.setCacheable(false);
resolver.setTemplateMode(TemplateMode.HTML);
return resolver;
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**", "/css/**")
.addResourceLocations("/WEB-INF/resources/", "/WEB-INF/css/");
}
请注意,如果我们使用的是Spring Boot,则可能不需要此配置,除非我们需要应用自己的自定义设置。
3.2 控制器
在这种情况下,控制器只是视图的载体。每个视图显示不同的片段使用场景。
最后一个加载一些通过模型传递的数据以显示在视图上:
@Controller
public class FragmentsController {
@GetMapping("/fragments")
public String getHome() {
return "fragments.html";
}
@GetMapping("/markup")
public String markupPage() {
return "markup.html";
}
@GetMapping("/params")
public String paramsPage() {
return "params.html";
}
@GetMapping("/other")
public String otherPage(Model model) {
model.addAttribute("data", StudentUtils.buildStudents());
return "other.html";
}
}
请注意,由于我们配置解析器的方式,视图名称必须包含“.html”后缀。 当我们引用片段名称时,我们还将指定后缀。
4. 观点
4.1 简单片段包含
首先,我们将在页面中重用公共部分。
我们可以将这些部分定义为片段,可以在独立的文件中,也可以在公共页面中。在这个项目中,这些可重用的部分被定义在一个名为fragments的文件夹中。
包含片段内容的三种基本方法:
- insert:在标签内插入内容
- replace:用定义片段的标签替换当前标签
- include:这已被弃用,但它可能仍会出现在遗留代码中
下一个示例fragments.html显示了所有三种方式的使用。这个Thymeleaf模板在文档的头部和主体中添加了片段:
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Thymeleaf Fragments: home</title>
<!--/*/ <th:block th:include="fragments/general.html :: headerfiles">
</th:block> /*/-->
</head>
<body>
<header th:insert="fragments/general.html :: header"> </header>
<p>Go to the next page to see fragments in action</p>
<div th:replace="fragments/general.html :: footer"></div>
</body>
</html>
现在,让我们看一下包含一些片段的页面。它被称为general.html,它就像一个完整的页面,其中一些部分定义为准备使用的片段:
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:fragment="headerfiles">
<meta charset="UTF-8"/>
<link th:href="@{/css/styles.css}" rel="stylesheet">
</head>
<body>
<div th:fragment="header">
<h1>Thymeleaf Fragments sample</h1>
</div>
<p>Go to the next page to see fragments in action</p>
<aside>
<div>This is a sidebar</div>
</aside>
<div class="another">This is another sidebar</div>
<footer th:fragment="footer">
<a th:href="@{/fragments}">Fragments Index</a> |
<a th:href="@{/markup}">Markup inclussion</a> |
<a th:href="@{/params}">Fragment params</a> |
<a th:href="@{/other}">Other</a>
</footer>
</body>
</html>
<head>部分仅包含一个样式表,但我们可以直接或使用Webjars应用其他工具,例如Bootstrap、jQuery或Foundation。
请注意,此模板的所有可重用标签都具有th:fragment属性,但接下来我们将了解如何包含页面的任何其他部分。
渲染和片段包含后,返回内容为:
<!DOCTYPE HTML>
<html>
<head>
<title>Thymeleaf Fragments: home</title>
<meta charset="UTF-8"/>
<link href="/spring-thymeleaf/css/styles.css" rel="stylesheet">
</head>
<body>
<header>
<div>
<h1>Thymeleaf Fragments sample</h1>
</div>
</header>
<p>Go to the next page to see fragments in action</p>
<footer>
<a href="/spring-thymeleaf/fragments">Fragments Index</a> |
<a href="/spring-thymeleaf/markup">Markup inclussion</a> |
<a href="/spring-thymeleaf/params">Fragment params</a> |
<a href="/spring-thymeleaf/other">Other</a>
</footer>
</body>
</html>
4.2 片段的标签选择器
Thymeleaf Fragments的一大优点是我们还可以仅使用简单的选择器、类、ID或简单地通过标签来获取模板的任何部分。
例如,此页面包含来自general.html文件的一些组件:aside块和div.another块:
<body>
<header th:insert="fragments/general.html :: header"> </header>
<div th:replace="fragments/general.html :: aside"></div>
<div th:replace="fragments/general.html :: div.another"></div>
<div th:replace="fragments/general.html :: footer"></div>
</body>
4.3 参数化片段
我们可以将参数传递给 片段以更改它的某些特定部分。为此,片段必须定义为函数调用,我们必须在其中声明参数列表。
在此示例中,我们为通用表单字段定义了一个片段:
<div th:fragment="formField (field, value, size)">
<div>
<label th:for="${#strings.toLowerCase(field)}"> <span
th:text="${field}">Field</span>
</label>
</div>
<div>
<input type="text" th:id="${#strings.toLowerCase(field)}"
th:name="${#strings.toLowerCase(field)}" th:value="${value}"
th:size="${size}">
</div>
</div>
这是我们向其传递参数的片段的简单用法:
<body>
<header th:insert="fragments/general.html :: header"></header>
<div th:replace="fragments/forms.html
:: formField(field='Name', value='John Doe',size='40')">
</div>
<div th:replace="fragments/general.html :: footer"></div>
</body>
这就是返回字段的外观:
<div>
<div>
<label for="name"> <span>Name</span>
</label>
</div>
<div>
<input type="text" id="name"
name="name" value="John Doe"
size="40">
</div>
</div>
4.4 片段包含表达式
Thymeleaf片段提供了其他有趣的选项,例如支持条件表达式以确定是否包含片段。
将Elvis运算符与Thymeleaf提供的任何表达式(例如安全性、字符串和集合)结合使用,我们能够加载不同的片段。
例如,我们可以使用我们将根据给定条件显示的一些内容来定义此片段。这可能是一个包含不同类型块的文件:
<div th:fragment="dataPresent">Data received</div>
<div th:fragment="noData">No data</div>
这就是我们如何用表达式加载它们:
<div
th:replace="${#lists.size(data) > 0} ?
~{fragments/menus.html :: dataPresent} :
~{fragments/menus.html :: noData}">
</div>
要了解有关Thymeleaf表达式的更多信息,请在此处查看我们的文章。
4.5 灵活的布局
下一个示例还展示了片段的另外两个有趣的用途,用于呈现包含数据的表格。这是可重用的表格片段,包含两个重要部分:可以更改的表格标题和呈现数据的主体:
<table>
<thead th:fragment="fields(theadFields)">
<tr th:replace="${theadFields}">
</tr>
</thead>
<tbody th:fragment="tableBody(tableData)">
<tr th:each="row: ${tableData}">
<td th:text="${row.id}">0</td>
<td th:text="${row.name}">Name</td>
</tr>
</tbody>
<tfoot>
</tfoot>
</table>
当我们要使用这个表时,我们可以使用fields函数传递我们自己的表头。标头由类myFields引用。通过将数据作为参数传递给tableBody函数来加载表体:
<body>
<header th:replace="fragments/general.html :: header"></header>
<table>
<thead th:replace="fragments/tables.html
:: fields(~{ :: .myFields})">
<tr class="myFields">
<th>Id</th>
<th>Name</th>
</tr>
</thead>
<div th:replace="fragments/tables.html
:: tableBody(tableData=${data})">
</div>
</table>
<div th:replace="fragments/general.html :: footer"></div>
</body>
这就是最终页面的外观:
<body>
<div>
<h1>Thymeleaf Fragments sample</h1>
</div>
<div>Data received</div>
<table>
<thead>
<tr class="myFields">
<th>Id</th>
<th>Name</th>
</tr>
</thead>
<tbody>
<tr>
<td>1001</td>
<td>John Smith</td>
</tr>
<tr>
<td>1002</td>
<td>Jane Williams</td>
</tr>
</tbody>
</table>
<footer>
<a href="/spring-thymeleaf/fragments">Fragments Index</a> |
<a href="/spring-thymeleaf/markup">Markup inclussion</a> |
<a href="/spring-thymeleaf/params">Fragment params</a> |
<a href="/spring-thymeleaf/other">Other</a>
</footer>
</body>
5. 总结
在本文中,我们展示了如何通过使用Thymeleaf Fragments重用视图组件,Thymeleaf Fragments是一种可以简化模板管理的强大工具。
我们还介绍了一些超出基础知识的其他有趣功能。在选择Thymeleaf作为我们的视图渲染引擎时,我们应该考虑这些因素。
如果你想了解Thymeleaf的其他功能,你一定要看看我们关于布局方言的文章。
与往常一样,本教程的完整源代码可在GitHub上获得。