Thymeleaf中提供了一套标准方言,这种方言提供了各种处理器,这些处理器可以应用特定的逻辑到某个组件(标签、文本、注释等)对象。并且,对于大多数用户来说,这套标准方言足以满足各种逻辑表达的需求。
官方提供的Thymeleaf-spring方言,可以刚好地利用spring framework中的一些特性。例如,使用Spring Expression Language 或者Spring EL 代替OGNL。
这些标准方言里的处理器,都属于属性处理器,这使得浏览器在处理之前也能正确显示HTML模板文件内容,因为对于未定义的额外属性,会被直接忽略。这样可以帮助设计人员和开发人员使用完全相同的模板文件,减少将静态原型转换为工作模板文件所需的各项工作。我们称此为:自然模板。
这里将从一下几个方面介绍Thymeleaf的基本语法:
- Thymeleaf 表达式
- Thymeleaf支持的字面值
- Thymeleaf支持的运算
- Thymeleaf常用的th:*属性
- Thymeleaf表达式基本对象
- Thymeleaf表达式工具对象
Thymeleaf 表达式
作为Thymeleaf标准方言最重要的组成部分,Thymeleaf提供了各种表达式,用于定制和现实不同类型的内容。
- 变量表达式 ${...}
- 选择变量表达式(星号表达式)*{...}
- 消息表达式 #{...}
- URL链接表达式 @{...}
- 片段表达式 ~{...}
变量表达式 ${...}
变量表达式用于显示某个变量的值,可以是普通表达式的计算结果,或者是服务器端返回的某个属性的值。
例如:
在ThymeleafController.java中添加以下代码,用于返回数据到视图
/**
* 这里我们需要使用@Controller注解:因为这个类返回的是视图(View)而不是数据(Data)
*/
@Controller
public class ThymeleafController {
@GetMapping("/index")
public String index(Model model, HttpSession session){
//在model中来存放要返回给客户端的数据key:value,在客户端中通过key获取对应的value
model.addAttribute("message","Hello World!!!");
model.addAttribute("result",100+200);
model.addAttribute("user",new User("alice",123456L,"email","M"));
session.setAttribute("user2",new User("admin",987654L,"admin@niit.com.cn","L"));
return "index";//逻辑视图名
}
}
在index.html模板中添加变量表达式显示各种数据
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1 th:text="${message}">Hi</h1>
<h1 th:text="${result}">计算结果</h1>
<h1 th:text="${1>2}">计算结果</h1>
<h1 th:text="${10*20}">计算结果</h1>
<div>
<h1 th:text="${user}">user对象</h1>
<h1 th:text="${user.username}">显示用户名</h1>
<h1 th:text="${user.pwd}">显示用户密码</h1>
</div>
<div>
<h1 th:text="${session.user2}">显示session作用域中的user2对象</h1>
<h1 th:text="${session.user2.username}">显示session作用域中的user2对象的用户名</h1>
</div>
<h1 th:text="${'id:'+user.username+',pwd:'+user.pwd}">user information</h1>
</body>
</html>
测试后发现${}的内容会作为表达式处理,${message}显示服务器返回的 message 属性的值, ${10*20}和${1>2}作为常规表达式被计算后显示计算结果在页面上,${result}显示 result 属性 的值,${user.username}和${user.pwd}分别显示 user 属性(一个 User 对象)中 username和 pwd属性的值,${session.user2}和${session.user2.username}分别显示session范围属性 user2 ( 一 个 User 对 象 ) 的 username 属 性 的 值 , session 为 内 置 对 象 , 最 后 的 ${'id:'+user.username+',pwd:'+user.pwd}表达式中,单引号内容为要连接的字符串, 字符串连接使用+运算符实现。
选择变量表达式(星号表达式)*{...}
该表达式用于内层引用外层对象,从而简化表达式的书写,列入对于变量表达式中我么书写用户对象的方式${user.useranme}和${user.pwd}可以用以下代码实现。
<div th:object="${user}">
<h1 th:text="*{username}">显示用户名</h1>
<h1 th:text="*{pwd}">显示用户密码</h1>
</div>
在上述代码中,可以在要使用选择表达式的内容外部嵌套一个 div 或其他标记,并使用 th:object 属性 和变量表达式指定引用的对象,也就是和一个对象进行绑定,这样在内容部分直接使用*{}表达式根 据属性名获取对应的属性值即可,无需再写属性的完整表达式。
消息表达式 #{...}
消息表达式可用于读取和显示文本文件中的内容,如属性文件,主要用于显示读取的文本消息或实 现国际化。
例如:在 src/main/resources 目录下新建一个目录,用于存放一些属性文件,假设目录名叫做 props, 在目录下新建一个属性文件,假设名为 tips.properties,用于存储一些提示信息,添加如下代码:
msg.welcome=Hi,欢迎来学习Thymeleaf
msg.error=Sorry,网站出现了一些异常,请等待工程师维护...
其中 msg.welcome 和 msg.error 为键的名称,也就是属性名称,等号后面的内容分别是两个属性的 值,后续可以根据键的名称获取值。
然后,在 application.yml中添加如下代码:
spring:
messages:
basename: props/tips
其中,spring .messages.basename 属性用于指定属性文件的位置,书写方式为文件路径方式 (props/tips),而不是包名方式(props.tips),props 为属性文件所在的目录,tips 为属性文件的名称,如 果有多个属性文件,可以用逗号隔开,分别给出每个属性文件的位置,如 props/tips, props/index, i18n/index/index。
最后,在模板页面上使用消息表达式读取和显示属性文件中的信息:
<p th:text="#{msg.welcome}"></p>
<p th:text="#{msg.error}"></p>
运行效果
对于实现国际化,则需要根据用户选择的显示语言,发送请求参数,配置 LocaleResolver,应用请求 参数的值,进而读取对应的属性文件的内容,显示在页面上。
URL 链接表达式: @{...}
在 web 开发中,页面中不可避免的要使用资源链接,不管引入 css、js、图片等,还是设置超链接的 href 属性或图片的 src 属性。为了区分请求的 url,很多时候我们会给请求添加一定的前缀,如学生请 求添加/studens 作为前缀,管理员可以添加/master 作为前缀,诸如此类。添加前缀导致的一个问题是 随着请求 url 的不断变化,访问使用了相对路径的资源可能会遇到 404 错误,这样开发人员就需要考 虑在页面中使用上下文路径来设置资源链接。Thymeleaf 提供链接表达式来实现资源的引用,这样不 论请求的 url 如何变化,链接的资源的路径总会是正确的。
下面的例子列举了使用链接表达式的链接:
<head>
<link rel="stylesheet" th:href="@{/css/bootstrap.min.css}">
<link rel="stylesheet" th:href="@{/css/login.css}">
<script th:src="@{/js/jquery-3.4.1.min.js}"></script>
<script th:src="@{/js/bootstrap.min.js}"></script>
</head>
<body>
<div class="container">
<img th:src="@{/images/1.jpg}" width="200" height="150"> <br><br>
<a th:href="@{https://www.baidu.com}">Baidu</a><br>
<a th:href="@{/toIndex}">Home</a><br>
<a th:href="@{/product(pid=P001)}">View details</a><br>
<a th:href="@{/product(pid=${proId})}">View details</a><br>
<a th:href="@{/search1(keyword=Sam,type=1)}">search1</a><br>
<a th:href="@{/search2/{pid}(pid=P001)}">search2</a><br>
<a th:href="@{/search3/{pid}/{type}(pid=${proId},type=1)}">
search3
</a><br>
<form th:action="@{/login}">
Username: <input type="text" name="uname"><br><br>
Password: <input type="text" name="pwd"><br><br>
<button>Login</button>
</form>
</div>
</body>
对于不同类型的url的表示,见下表(假设端口号为8088,上下文路径为"/")
路径 | 表达式写法 | 处理结果 | 访问路径 |
---|---|---|---|
绝对路径 | @{http://www.baidu.com} | http://www.baidu.com | http://www.baidu.com |
页面相对路径 | @{login} | login | http://localhost:8088/login |
上下文相对路径(/开头) | @{/login} | /login | http://localhost:8088/login |
服务器相对路径(~开头) | @{~/login} | /login(如果自定义了上下文路径,将不显示上下文路径) | http://localhost:8088/login |
协议相对路径(//开头) | @{//login} | //login | http://login |
片段表达式:~{...}
片段(Fragment)表达式用于定义模板内容,并在模板页面中引用模板页或模板片段,相当于定义模板 内容,然后可以将该片段在任何模板页面的任何地方使用。例如:我们需要在内容页引入头部和底部页面。
使用片段表达式有以下几个步骤:
- 使用th:fragment标记被引用的部分
- 使用th:insert 或者th:replace在主页面引用被fragment标记的部分。
- th:insert: 插入要引入的代码,作为当前标签的子标签
- th:replace 使用引入的代码替换当前的标签。
在 src/main/resources/templates 目录下新建一个模板页面 header.html和footer.html,页面中使用以下代码定义 了一个模板片段:
<!--src/main/resources/templates/header.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>页面头部</title>
</head>
<body>
<h3 th:fragment="headpage">欢迎来到Thymeleaf的课堂</h3>
</body>
</html>
<!--src/main/resources/templates/footer.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>页面底部</title>
</head>
<body>
<div th:fragment="footpage">
Alice All Right Reserved <br/>
© China ICP No5532-0968
</div>
</body>
</html>
在上面的页面中 th:fragment定义了一个可用作模板内容的片段,名为 headpage(footpage)。如 果想在其他页面使用该模板片段,直接使用 th:insert 或 th:replace 属性联合片段表达式~{}引用模板片段 即可,代码如下:
在同目录下创建main.html.引入页面头部和底部的代码。这里分别使用th:insert和th:replace
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>主页</title>
</head>
<body>
<!--引入页头-->、
<header th:insert="~{header::headpage}"></header>
<!--页面内容部分-->
<section>
<div>
这里显示页面的主要内容
</div>
</section>
<!--引入页脚-->
<footer th:replace="~{footer::footpage}"></footer>
</body>
</html>
其中 th:insert 属性用于插入一个模板片段的内容,书写使用~{}表达式,表达式中 header(footer) 表示模板片 段所在的页面,headpage(footpage)为模板片段的名称,中间使用::连接。
运行 main.html 页面,可看到页面插入了模板片段的内容。
th:insert与th:replace的区别:
th:insert 属性是插入内 容,th:replace 属性是替换内容,也就是说 th:insert 属性是在 <header></header> 标签中插入模板片段, 而 th:replace 属性是直接用模板片段替换<footer></footer>标签,footer元素将不复存在。
另一种用法是不使用th:fragment片段,直接给片段内容定义一个id,如<div th:fragment="footpage">替换为<div id="foodid">,在使用模板时,则使用<footer th:replace="~{footer::#footid}">来引入模板片段。
关于片段表达式的更多书写格式,可参考下表:
表达式书写 | 说明 | 示例 |
---|---|---|
~{templatename::selector} | 引用名为 templatename 的模板 页面中名为 selector 的片段 | ~{footer::footpage} |
~{templatename::#selector} | 引用名为 templatename 的模板 页面中 id 为 selector 的片段 | ~{footer::#footpage} |
~{templatename} | 引用名为 templatename 的模 板页面的完整内容 | ~{footer} |
~{::selector}或 ~{this::selector} | 引用当前模板页面中名为 selector 的片段 | ~{::contentDiv} ~{this::contentDiv} |
~{::#selector}或 ~{this::#selector} | 引用当前模板页面中 id 为 selector 的片段 | 引用当前模板页面中 id 为 selector 的片段 |
下一篇,介绍Thymeleaf语法(二)