官方文档:https://www.thymeleaf.org/doc/tutorials/2.1/usingthymeleaf.html#standard-expression-syntax
参考博客:http://www.ityouknow.com/springboot/2016/05/01/spring-boot-thymeleaf.html
首先,我们看一下标准表达式功能的快速摘要。
(1)简单表达式
- 变量表达式:${...}
- 选择变量表达式:*{...}
- 国际化(消息)表达式:#{...}
- URL表达式:@{...}
(2)字面(Literals)
- 文本:'one text' 'another one'
- 数字:0 34 3.0
- 布尔(Boolean):true false
- 空:null
- 文字标记:one sometext main...
(3)文本操作
- 字符串连接:+
- 文字替换:|名称是${userName}|
(4)算术运算
- 二元运算:+ - * / %
- 减号(一元运算符):-
(5)布尔操作
- 二元运算:and or
- 布尔否定(一元运算符):! not
(6)比较和等价
- 比较:> < >= <= (gt lt ge le)
- 等值运算符:== != (eq ne)
条件运算符
- If-then:(if) ? (then)
- If-then-else:(if) ? (then) : (else)
- Default:(value) ?: (defaultvalue)
所有这些功能可以组合和嵌套使用:
'用户角色是:' + (${user.isAdmin()} ? '管理员' : (${user.type} ?: '未知角色'))
1. 国际化
我们已经知道#{...}消息表达式允许我们链接:
<p th:utext="#{home.welcome}">欢迎来到商店!</p>
在home_en.properties配置了该key
home.welcome=¡Bienvenido a nuestra tienda de comestibles!
这样的写法适用于纯静态,但是用户已经登录了,我们如何动态的取出用户姓名?
取出这个名字需要我们添加一个参数到我们的message上
home.welcome=¡Bienvenido a nuestra tienda de comestibles, {0}!
参数根据java.text.MessageFormat标准表达式指定,这也意味着你可以给数字或日期添加指定的格式。
为了给参数一个具体的值,在session中添加user,写法如下:
<p th:utext="#{home.welcome(${session.user.name})}">
Welcome to our grocery store, Sebastian Pepper!
</p>
假如必要的话,可以定义多个参数,以英文逗号分隔。实际上,key本身可以来自变量:
<p th:utext="#{${welcomeMsgKey}(${session.user.name})}">
Welcome to our grocery store, Sebastian Pepper!
</p>
2. 变量
${...}表达式实际是在上下文中包含的map执行OGNL(Object-Graph Navigation Language:对象图导航语言)表达式。OGNL详细用法参考:http://commons.apache.org/ognl/
getter方法导航是OGNL的用法之一,更多用法如下:
/*
* 使用.访问属性,等价于调用getter方法
*/
${person.father.name}
/*
* 也可以通过[]访问属性,属性名称在单引号间作为一个变量
*/
${person['father']['name']}
/*
* 假如对象是map,使用 . 或 []语法都等价于调用get(...)方法
*/
${countriesByCode.ES}
${personsByName['LM'].age}
/*
* 使用下标方式访问数组或集合
*/
${personsArray[0].name}
/*
* 通过参数执行方法
*/
${person.createCompleteName()}
${person.createCompleteNameWithSeparator('-')}
2.1 表达式基本对象
在上下文变量上使用OGNL表达式时,一些对象可以用于表达式从而获得更高的灵活性。 使用#号将引用这些对象(按照OGNL标准),主要有以下几个对象,其他对象及使用方法参考官方文档附录A:
- #ctx:上下文对象
- #vars:上下文变量
- #local:上下文locale
- #httpServletRequest:(web上下文)HttpServletRequest对象
- #httpSession:(web上下文)HttpSession对象
示例:
当前国家: <span th:text="${#locale.country}">中国</span>.
2.2 表达式工具对象
除了上面的基本对象,Thymeleaf还提供了一系列工具对象帮助我们在表达式处理一些常见任务,所有这些对象的方法参考官方文档的附录B:https://www.thymeleaf.org/doc/tutorials/2.1/usingthymeleaf.html#appendix-b-expression-utility-objects
主要有下面这些工具对象:
#dates
:java.util.Date
对象实用方法: 格式转换等#calendars
: 类似于#dates
, 但是java.util.Calendar对象
#numbers
: 格式化数字实用方法#strings
: 字符串实用方法: contains, startsWith, prepending/appending等#objects
: 对象实用方法#bools
: boolean实用方法#arrays
: 数组实用方法#lists
: list列表实用方法#sets
: set集合实用方法#maps
: map集合实用方法#aggregates
: 数组或集合上创建聚合的实用方法#messages
: 在变量表达式中获取外部化消息的实用方法,与使用#{...}语法获得的方法相同#ids
: 处理可能重复的id属性的实用方法(例如,作为迭代的结果)
2.3 示例 - 格式化日期
<p>
今天是: <span th:text="${#calendars.format(today,'yyyy-MM-dd HH:mm:ss')}">2018-08-10 07:45:00</span>
</p>
3. 选择表达式(星号表达式)
变量表达式不仅可以用${...}表达式,还可以使用*{...}表达式。
星号表达式在选定的对象中执行而不是整个上下文map变量。但是只要没有选定对象,${...}和*{...}没有区别。
示例:
<div th:object="${session.user}">
<p>姓名: <span th:text="*{name}">小明</span>.</p>
<p>性别: <span th:text="*{sex}">男</span>.</p>
<p>年龄: <span th:text="*{age}">18</span>.</p>
</div>
等价于:
<div>
<p>姓名: <span th:text="${session.user.name}">小明</span>.</p>
<p>性别: <span th:text="${session.user.sex}">男</span>.</p>
<p>年龄: <span th:text="${session.user.age}">18</span>.</p>
</div>
${...}和*{...}可以混合使用:
<div th:object="${session.user}">
<p>姓名: <span th:text="${#object.name}">小明</span></p>
<p>性别: <span th:text="${session.user.sex}">男</span></p>
<p>年龄: <span th:text="*{age}">18</span></p>
</div>
没有选定对象,${...}和*{...}等价
<div>
<p>姓名: <span th:text="*{session.user.name}">小明</span>.</p>
<p>性别: <span th:text="*{session.user.sex}">男</span>.</p>
<p>年龄: <span th:text="*{session.user.age}">18</span>.</p>
</div>
4. URL表达式
由于它们的重要性,URL是Web应用程序模板中的'一等公民',Thymeleaf标准方言有一个特殊的语法,@ 语法:@ {...}
不同类型的URL:
- 绝对路径:例如http://www.thymeleaf.org
- 相对路径:
- 相对于当前页面:user/login.html
- 相对于上下文:/itemdetails?id=3,服务器中应用上下文名称(项目名称)会被自动添加,
- 相对于服务器:~/billing/processInvoice,允许在同一服务器链接到另外的上下文
- 相对于协议://code.jquery.com/jquery-2.0.3.min.js
Thymeleaf可以在任何情况下处理绝对URL,但对于相对URL,它要求使用实现IWebContext接口的上下文对象,该接口包含来自HTTP请求的一些信息以及创建相对链接所需的信息。
示例:
<!-- 结果:'http://localhost:8080/projName/order/details?orderId=3' -->
<a href="details.html"
th:href="@{http://localhost:8080/gtvg/order/details(orderId=${o.id})}">查看</a>
<!-- 结果:'/projName/order/details?orderId=3' -->
<a href="details.html" th:href="@{/order/details(orderId=${o.id})}">查看</a>
<!-- 结果:'/projName/order/3/details' -->
<a href="details.html" th:href="@{/order/{orderId}/details(orderId=${o.id})}">查看</a>
提示:
- th:href是一个属性修饰符属性:一旦处理,它将会计算使用的URL,并将<a>标签的href属性设置为该值。
- 允许URL参数上使用表达式(例如:orderId=${o.id}),自动执行URL编码操作
- 假如需要设置多个参数,使用逗号分隔,例如:@{/order/process(id=${id},type='fast')}
- URL路径中允许使用变量模板,例如:@{/order/{orderId}/details(orderId=${orderId})}
- 以 / 开头的相对URL(如:/order/details),自动会添加应用上下文名称(项目名称)
- 假如未启用cookie或不知道cookie,一个';jsessionid=...'可以被添加为相对路径的后缀,确保session会话保留。这被称作URL重写,Thymeleaf允许通过Servlet API为每个URL使用response.encodeURL(...)机制来插入自己的重写过滤器。
- th:href标签允许我们(可选的)在我们的模板中有一个静态的href属性,便于我们在设计静态页面时浏览器仍然可以打开链接。
与消息语法(#{...})一样,URL也可以是表达式的结果:
<a th:href="@{${url}(orderId=${o.id})}">查看</a>
<a th:href="@{'/details/'+${user.login}(orderId=${o.id})}">查看</a>
4.1 主页菜单
我们知道了如何创建url,在我们主页添加一个小菜单用于打开其他页面:
<p>请选择</p>
<ol>
<li><a href="product/list.html" th:href="@{/product/list}">产品列表</a></li>
<li><a href="order/list.html" th:href="@{/order/list}">订单列表</a></li>
<li><a href="subscribe.html" th:href="@{/subscribe}">意见反馈</a></li>
<li><a href="userprofile.html" th:href="@{/userprofile}">用户信息</a></li>
</ol>
4.2 相对于服务器根目录的URL
可以另外一个语法来创建相对于服务器根目录的(不是相对于上下文)URL,以便链接到同一服务器中的不同上下文。 这些URL将被定义为:@ {〜/path/ o/something}
5. 字面量
5.1 文本字面量
文本字符就是字符串,定义在单引号间。这之间可以包含任意字符,假如是单引号则需要使用转移:\'
<p>
文本字面量:<span th:text="'web应用'"></span>.
</p>
5.2 数字字面量
数字字面量也就是数字
<p>今年是<span th:text="2018">2018</span>.</p>
<p>两年后将会是<span th:text="2018 + 2">2020</span>.</p>
5.3 Boolean字面量
包括true和false,例如
<div th:if="${user.isAdmin()} == false"> ...
提示:在上面的例子中,==false写在大括号之外,Thymeleaf本身负责处理它;如果实在大括号中,它将由OGNL/SpringEL负责处理。
<div th:if="${user.isAdmin() == false}"> ...
5.4 null字面量
null字面量也可以向上面例子一样使用:
<div th:if="${variable.something} == null"> ...
5.5 文本标记
数字、boolean、null字面量是文本标记的特例。
这些文本标记允许在标准表达式中进行一些简化。 它们与文本文字('...')完全相同,但只允许字母(A-Z和a-z),数字(0-9),括号([ ]),点(.),连字符( - ) 和下划线(_)。 所以没有空格、逗号等。
好处?文本标记不需要任何的单引号括起来。所以我们可以这样做:
<div th:class="content">...</div>
<!-- 上面的写法替代下面的 -->
<div th:class="'content'">...</div>
6. 文本拼接
文本,不管是字面量或变量表达式执行结果或消息表达式,都可以使用 + 操作:
th:text="'姓名:' + ${user.name}"
7. 文字替换
文字替换可以轻松地格式化包含变量值的字符串,不需要使用+操作。这些替换需要用| |括起来,例如:
<span th:text="|欢迎, ${user.name}!|">
上面的写法等价于:
<span th:text="'欢迎, ' + ${user.name} + '!'">
文字替换可以结合其他类型的表达式:
<span th:text="${onevar} + ' ' + |${twovar}, ${threevar}|">
提示:只有变量表达式${...}允许写在| |里面,('...')、boolean、数字标记和条件表达式不允许。
8. 算术运算
支持一些算术运算:+ - / * %
th:with="isEven=(${prodStat.count} % 2 == 0)"
这些运算也可以写在OGNL表达式内,这种情况下由OGNL负责执行,而不是Thymeleaf标准表达式yin引擎:
th:with="isEven=${prodStat.count % 2 == 0}"
提示:其中一些运算符存在文本别名:div(/)、mod(%)
9. 比较和相等
在表达式中,值可以通过> < >= <=进行比较,还可以通过== !=运算符比较相等或不等。提示:XML的< >符号不能再属性中使用,使用它们应该替换为< $gt;
th:if="${prodStat.count} > 1"
th:text="'执行模式是:' + ( (${execMode} == 'dev')? '开发环境' : '生产环境')"
提示:其中一些运算符存在文本别名:gt(>) lt(<) ge(>=) lw(<=) not(!) eq(==) neq/ne(!=)
10. 条件表达式
条件表达式用于判断两个表达式中的一个,取决于判断的结果。示例:(th:class :class属性修饰符)
<tr th:class="${row.even}? 'even' : 'odd'">
...
</tr>
条件表达式的三个部分(condition then 和 else),它们本身也是表达式,这就意味着它们可以是变量${....},*{...},消息#{...},URL(@{...})或者字面量('...')
条件表达式可以使用括号嵌套:
<tr th:class="${row.even}? (${row.first}? 'first' : 'even') : 'odd'">
...
</tr>
Else表达式可以省略,这种情况下,如果条件为false,返回null
<tr th:class="${row.even}? 'alt'">
...
</tr>
11. 默认表达式(Elvis运算符)
默认表达式是一种特殊条件值,它没有then部分。它等价于一些语言中的Elvis 运算符,例如Groovy,允许指定两个表达式,第二个表达式在第一个表达式返回null时计算。
示例:
<div th:object="${session.user}">
...
<p>年龄: <span th:text="*{age}?: '(未获取到年龄)'">18</span>.</p>
</div>
如上面例子所示,运算符?:,当*{age}是null时,我们可以使用它指定一个默认值。等价于下面的写法:
<p>年龄: <span th:text="*{age != null}? *{age} : '(未获取到年龄)'">18</span>.</p>
与条件表达式一样,可以通过括号嵌套表达式:
<p>
姓名:
<span th:text="*{name}?: (*{admin}? '管理员' : #{default.name})">小明</span>
</p>
12. 预处理
除了用于处理表达式的功能外,Thymeleaf还为我们提供了预处理表达式。
预处理可以做什么? 它在正常表达式之前完成表达式的执行,它允许修改最终实际执行的表达式。
预处理表达式与普通表达式完全相同,使用双下划线包围,如: __ $ {expression} __
假设我们有一个i18n Messages_fr.properties配置文件,其中包含一个OGNL表达式,调用特定语言的静态方法,如:
article.text=@myapp.translator.Translator@translateToFrench({0})
es(西班牙) Messages_es.properties配置文件如下
:
article.text=@myapp.translator.Translator@translateToSpanish({0})
我们可以创建一个标记片段,根据语言环境判断一个表达式或另一个表达式。 为此,我们将首先选择表达式(通过预处理),然后让Thymeleaf执行它:
<p th:text="${__#{article.text('textVar')}__}">其他内容...</p>
提示:法语区域设置的预处理步骤将创建以下等效项:
<p th:text="${@myapp.translator.Translator@translateToFrench(textVar)}">其他内容...</p>
如果在属性中要使用预处理字符串'__',可以使用'\_\_'进行转义。