1. Overview
1.概述
In this tutorial, we’ll show how to make use of Thymeleaf Fragments to reuse some common parts of a site. After setting up a very simple Spring MVC project, we’ll focus on views.
在本教程中,我们将展示如何利用Thymeleaf Fragments来重用网站的一些常用部分。在建立了一个非常简单的Spring MVC项目后,我们将专注于视图。
If you’re new to Thymeleaf, you can check other articles on this site like this introduction, as well as this one about the 3.0 version of the engine.
如果你是Thymeleaf的新手,你可以查看本网站的其他文章,如这个介绍,以及这个关于3.0版本引擎的文章。
2. Maven Dependencies
2.Maven的依赖性
We’ll need a couple of dependencies to enable Thymeleaf:
我们需要几个依赖项来启用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>
The latest version of thymeleaf and thymeleaf-spring5 can be found on Maven Central.
thymeleaf和thymeleaf-spring5的最新版本可以在Maven中心找到。
3. Spring Project
3.Spring项目
3.1. Spring MVC Configuration
3.1.Spring MVC配置
To enable Thymeleaf and set the template suffix, we need to configure MVC with a view resolver and template resolver.
为了启用Thymeleaf并设置模板后缀,我们需要配置MVC的视图解析器和模板解析器。
We’ll also set the directory for some static resources:
我们还将为一些静态资源设置目录。
@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/");
}
Note that if we’re using Spring Boot, this configuration may not be necessary unless we need to apply our own customizations.
注意,如果我们使用的是Spring Boot,除非我们需要应用自己的定制,否则这种配置可能是不必要的。
3.2. The Controller
3.2.控制器
In this case, the controller is just a vehicle for the views. Each view shows a different fragment use scenario.
在这种情况下,控制器只是视图的一个载体。每个视图都显示了不同的片段使用场景。
The last one loads some data that is passed through the model to be displayed on the view:
最后一个加载一些数据,这些数据通过模型被显示在视图上。
@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";
}
}
Note that the view names must contain the “.html” suffix because of the way we configured our resolver. We’ll also specify the suffix when we refer to fragment names.
注意,由于我们配置解析器的方式,视图名称必须包含“.html”后缀。当我们提及片段名称时,我们也将指定后缀。
4. The Views
4.观点
4.1. Simple Fragment Inclusion
4.1.简单的片段包容
First of all, we’ll use reuse common parts in our pages.
首先,我们将在我们的页面中使用重复使用的公共部分。
We can define these parts as fragments, either in isolated files or in a common page. In this project, these reusable parts are defined in a folder named fragments.
我们可以将这些部分定义为片段,既可以在孤立的文件中,也可以在一个共同的页面中。在这个项目中,这些可重复使用的部分被定义在一个名为fragments的文件夹中。
There are three basic ways to include content from a fragment:
从片段中包含内容有三种基本的方式:。
- insert – inserts content inside the tag
- replace – replaces the current tag with the tag defining the fragment
- include – this is deprecated but it may still appear in a legacy code
The next example, fragments.html, shows the use of all three ways. This Thymeleaf template adds fragments in the head and the body of the document:
下一个例子,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>
Now, let’s take a look at a page that holds some fragments. It’s called general.html, and it’s like a whole page with some parts defined as fragments ready to be used:
现在,让我们来看看一个容纳了一些片段的页面。它被称为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>
The <head> section contains just a stylesheet, but we could apply other tools such as Bootstrap, jQuery, or Foundation, either directly or using Webjars.
<head>部分只包含一个样式表,但我们可以直接或使用Webjars应用其他工具,如Bootstrap、jQuery或Foundation。
Note that all the reusable tags of this template have the attribute th:fragment, but next we’ll see how to include any other part of the page.
注意,这个模板的所有可重用标签都有th:fragment属性,但接下来我们将看到如何包含页面的任何其他部分。
After the rendering and fragments inclusion, the returned content is:
在渲染和包含片段之后,返回的内容是。
<!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. Markup Selectors for Fragments
4.2.片段的标记选择器
One of the great things about Thymeleaf Fragments is that we can also grab any part of a template just using the simple selectors, through classes, ids, or simply by tags.
Thymeleaf Fragments的一个好处是,我们也可以通过简单的选择器,通过类、id或简单的标签,抓取模板的任何部分。
This page, for example, includes some components from general.html file: an aside block and the div.another block:
例如,这个页面包括general.html文件中的一些组件:一个aside块和div.other块。
<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. Parametrized Fragments
4.3.参数化片段
We can pass parameters to a fragment in order to change some specific part of it. To do that, the fragment must be defined as a function call, where we must declare a list of parameters.
我们可以向片段传递参数,以便改变它的某些特定部分。要做到这一点,该片段必须被定义为一个函数调用,我们必须声明一个参数列表。
In this example, we define a fragment for a generic form field:
在这个例子中,我们为一个通用表单字段定义了一个片段。
<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>
And here’s a simple use of that fragment where we pass parameters to it:
这里是该片段的一个简单使用,我们向它传递参数。
<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>
And this is how the returned field will look:
而这就是返回字段的样子。
<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. Fragment Inclusion Expressions
4.4.片段包容表达式
Thymeleaf fragments offer other interesting options such as support for conditional expressions to determine whether to include a fragment.
Thymeleaf片段还提供了其他有趣的选项,比如支持条件表达式来决定是否包含一个片段。
Using the Elvis operator with any of the expressions provided by Thymeleaf (security, strings, and collections for example), we’re able to load different fragments.
使用Elvis操作符和Thymeleaf提供的任何表达式(例如安全、字符串和集合),我们就能加载不同的片段。
For example, we can define this fragment with some content that we’ll show depending on a given condition. This could be a file containing different kinds of blocks:
例如,我们可以用一些内容来定义这个片段,我们将根据一个给定的条件来显示。这可能是一个包含不同种类的块的文件。
<div th:fragment="dataPresent">Data received</div>
<div th:fragment="noData">No data</div>
And this is how we could load them with an expression:
这就是我们如何用表达式加载它们。
<div
th:replace="${#lists.size(data) > 0} ?
~{fragments/menus.html :: dataPresent} :
~{fragments/menus.html :: noData}">
</div>
To learn more about Thymeleaf Expressions, check our article here.
要了解更多关于百里叶表达式的信息,请查看我们的文章这里。
4.5. Flexible Layouts
4.5.灵活的布局
The next example also shows two other interesting uses of fragments to render a table with data. This is the reusable table fragment, with two important parts: a table header that can be changed, and the body where data is rendered:
下一个例子还展示了片段的另外两种有趣的用法,即渲染一个有数据的表。这是可重复使用的表格片段,有两个重要部分:一个是可以改变的表头,另一个是渲染数据的主体。
<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>
When we want to use this table, we can pass our own table header using the fields function. The header is referenced with the class myFields. The table body is loaded by passing data as a parameter to the tableBody function:
当我们想使用这个表时,我们可以使用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>
And this is how the final page will look:
这就是最后一页的样子。
<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. Conclusion
5.结论
In this article, we’ve shown how to reuse view components through the use of Thymeleaf Fragments, a powerful tool that can make template management easier.
在这篇文章中,我们展示了如何通过使用Thymeleaf Fragments来重用视图组件,这是一个强大的工具,可以使模板管理更容易。
We have also presented some other interesting features that go beyond the basics. We should take these into account when choosing Thymeleaf as our view rendering engine.
我们还介绍了一些其他有趣的功能,这些功能超出了基本的范围。在选择Thymeleaf作为我们的视图渲染引擎时,我们应该把这些考虑进去。
If you want to learn about other Thymeleaf features, you should definitely take a look at our article about Layout Dialects.
如果你想了解Thymeleaf的其他功能,你一定要看看我们关于Layout Dialects的文章。
As always, the complete implementation code of the example is available over on GitHub.
一如既往,该示例的完整实施代码可在GitHub上获取。