1.注释
1.1标准HTML/XML注释
Thymeleaf不会处理这些注释中的任何内容,并将逐字复制到结果中。
<!-- User info follows -->
1.2Thymeleaf解析器级别的注释块
Thymeleaf将删除<!--/*
和*/-->
标记,以及标记之间的所有内容。
//形式一
<!--/* This code will be removed at Thymeleaf parsing time! */-->
//形式二
<!--/*-->
<div>
you can see me only before Thymeleaf processes me!
</div>
<!--*/-->
1.3Thymeleaf仅原型注释块
Thymeleaf将删除<!--/*/
和/*/-->
标记,但不会删除其内容。
//源代码
<span>hello!</span>
<!--/*/
<div th:text="${...}">
...
</div>
/*/-->
<span>goodbye!</span>
//解析时
<span>hello!</span>
<div th:text="${...}">
...
</div>
<span>goodbye!</span>
2.字面量
2.1数字字面量
<p>The year is <span th:text="2013">1492</span>.</p>
<p>In two years, it will be <span th:text="2013 + 2">1494</span>.</p>
2.2布尔字面量
//false写在大括号外面,所以 Thymeleaf 负责处理它
<div th:if="${user.isAdmin()} == false"> ...
//false写在大括号内,这将是 OGNL/SpringEL 引擎的责任
<div th:if="${user.isAdmin() == false}"> ...
2.3文本字面量
文本字面量就是定义在单引号之间的字符串。它们可以包含任何字符,但由于单引号是有意义的,如果需要使用单引号,您应该使用\'
转义其中的任何单引号。
<p>
Now you are looking at a <span th:text="'working web application'">template file</span>.
</p>
2.4null字面量
<div th:if="${variable.something} == null"> ...
2.5字面量标记
数字、布尔值和 null 字面量实际上是字面量标记的一种特殊情况。
这些标记允许在标准表达式中进行一些简化。它们的工作方式与文本字面量('...'
) 完全相同,但它们只允许使用字母 (A-Z
和 a-z
)、数字 (0-9
)、括号 ([
和 ]
)、点 (.
)、连字符 (-
) 和下划线 (_
)。所以没有空格,没有逗号等。
标记不需要任何引号。所以我们可以这样做:
<div th:class="content">...</div>
代替:
<div th:class="'content'">...</div>
3.运算符
3.1字符串连接
文本,无论是字面量还是计算变量或消息表达式的结果,都可以使用+
运算符轻松附加。
<span th:text="'The name of the user is ' + ${user.name}">
3.2字面量替换
字面量替换允许轻松格式化包含来自变量的值的字符串,而无需在字面量后面附加'...' + '...'
。
这些替换必须用竖线 ( |
) 包围,例如:
<span th:text="|Welcome to our application, ${user.name}!|">
这相当于:
<span th:text="'Welcome to our application, ' + ${user.name} + '!'">
字面量替换可以与其他类型的表达式组合:
<span th:text="${onevar} + ' ' + |${twovar}, ${threevar}|">
在字面量替换|...|
中只允许使用变量/消息表达式 ( ${...}
, *{...}
,#{...}
)。没有其它字面量('...'
)、布尔/数字标记、条件表达式等。
3.3算术运算
请注意,其中一些运算符存在文本别名:( div
) /
、mod
( %
)。
//负号(一元运算符)
-
//二元运算符
+, -, *, /, %
3.4比较和相等运算
//比较
>, <, >=, <= (>, <, &ge, &le)
//相等
==, != (&eq, &neq/&ne)
3.5布尔逻辑运算
//逻辑非(一元运算符)
!, not
//二元运算符
and, or
3.6条件运算
condition为真时,运行then,否则返回空。
// (condition)? (then)
<tr th:class="${row.even}? 'alt'">
...
</tr>
condition为真时,运行then,否则运行else。
// (condition)? (then) : (else)
<tr th:class="${row.even}? 'even' : 'odd'">
...
</tr>
3.7默认表达式
如果计算结果不为 null,则使用第一个表达式,如果结果为 null,则使用第二个表达式。
(value) ?: (defaultvalue)
//示例
<div th:object="${session.user}">
...
<p>Age: <span th:text="*{age}?: '(no age specified)'">27</span>.</p>
</div>
//等同于
<p>Age: <span th:text="*{age != null}? *{age} : '(no age specified)'">27</span>.</p>
3.8无操作运算
No-Operation 标记由下划线符号 (_
) 表示。
这个标记背后的想法是指定表达式的期望结果是什么都不做,即完全就好像可处理属性(例如th:text
)根本不存在一样。
<span th:text="${user.name} ?: 'no user authenticated'">...</span>
//更简洁和通用的代码
<span th:text="${user.name} ?: _">no user authenticated</span>
3.9数据转换/格式化
Thymeleaf为变量 ( ${...}
) 和选择 ( *{...}
) 表达式定义了双括号语法,允许我们通过配置的转换服务应用数据转换。
${{...}}
*{{...}}
4.表达式
4.1#{...}
消息表达式
//未传参
home.welcome=¡Bienvenido a nuestra tienda de comestibles!
<p th:utext="#{home.welcome}">Welcome to our grocery store!</p>
//传参
home.welcome=¡Bienvenido a nuestra tienda de comestibles, {0}!
<p th:utext="#{home.welcome(${session.user.name})}">
Welcome to our grocery store, Sebastian Pepper!
</p>
4.2${...}
变量表达式
//普通变量
<p>Today is: <span th:text="${today}">13 february 2011</span>.</p>
//对象变量
Established locale country: <span th:text="${#locale.country}">US</span>.
4.3*{...}
选择变量表达式
<div th:object="${session.user}">
<p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
<p>Surname: <span th:text="*{lastName}">Pepper</span>.</p>
<p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
</div>
//等同于
<div>
<p>Name: <span th:text="${session.user.firstName}">Sebastian</span>.</p>
<p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p>
<p>Nationality: <span th:text="${session.user.nationality}">Saturn</span>.</p>
</div>
4.4@{...}
链接 URL 表达式
//绝对URL
@{http://localhost:8080/gtvg/order/details(orderId=${o.id})}
//输出
http://localhost:8080/gtvg/order/details?orderId=3
//相对页面(相对URL)
@{user/login.html}
//相对上下文(相对URL)
@{/itemdetails?id=3}
@{/order/details(orderId=${o.id})}
//输出
/gtvg/order/details?orderId=3
@{/order/{orderId}/details(orderId=${o.id})}
//输出
/gtvg/order/3/details
//多个参数
@{/order/process(execId=${execId},execType='FAST')}
//相对服务器(相对URL)
@{~/billing/processInvoice}
//相对协议URL(相对URL)
@{//code.jquery.com/jquery-2.0.3.min.js}
4.5~{...}
片段表达式
~{...}
是可以省略的。
//引入某个模板文件中的某个选择器或片段
~{templatename::selector} 或 ~{templatename::fragmentname}
//引入某个模板文件
~{templatename}
//引入同一个模板文件中的选择器或片段
~{::selector} 或 ~{this::selector}
//引入空片段
~{}
<div th:insert="~{commons :: main}">...</div>
<div th:with="frag=~{footer :: #main/text()}">
<p th:insert="${frag}">
</div>
5.属性
5.1xmlns:th、th:href
xmlns:th
:所有th:*
属性的命名空间定义。
th:href
:超链接的目标URL。
<html xmlns:th="http://www.thymeleaf.org"></html>
th:href="@{/css/gtvg.css}"
5.2th:text、th:utext
th:text
:使用外部化文本替换元素标签之间的内容,外部化文本会被转义。
th:utext
:使用外部化文本替换元素标签之间的内容,外部化文本不会被转义。
th:text="外部化文本"
th:utext="外部化文本"
th:text
的内联简写对应于[[...]]
,th:utext
的内联简写对应于[(...)]
。能在th:text和th:utext中的表达式同等地在对应的内联语法中使用。
<p>Hello, <span th:text="${session.user.name}">Sebastian</span>!</p>
//内联简写
<p>Hello, [[${session.user.name}]]!</p>
msg = 'This is <b>great!</b>'
<p>The message is "[(${msg})]"</p>
//输出
<p>The message is "This is <b>great!</b>"</p>
//示例
home.welcome=Welcome to our <b>fantastic</b> grocery store!
<p th:text="#{home.welcome}">Welcome to our grocery store!</p>
//输出
<p>Welcome to our <b>fantastic</b> grocery store!</p>
<p th:utext="#{home.welcome}">Welcome to our grocery store!</p>
//输出
<p>Welcome to our <b>fantastic</b> grocery store!</p>
5.3th:attr、th:attrappend、th:attrprepend
th:attr
:设置任意属性的值。
<form action="subscribe.html" th:attr="action=@{/subscribe}">
<input type="submit" value="Subscribe!" th:attr="value=#{subscribe.submit}"/>
<img src="../../images/gtvglogo.png"
th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />
th:attrappend
和th:attrprepend
:将计算结果附加(后缀)或前置(前缀)到现有属性值。
例如,当CSS类将取决于用户所做的事情而不同时:
<input type="button" value="Do it!" class="btn" th:attrappend="class=${' ' + cssStyle}" />
当用户行为需要传递给cssStyle
变量为warning
时,可以为:
<input type="button" value="Do it!" class="btn warning" />
5.4th:*、th:alt-title、th:lang-xmllang
th:*
:th:attr
创建属性的方式不是很优雅,可以使用th:*
属性设置特定属性的值。
<form action="subscribe.html" th:action="@{/subscribe}">
<input type="submit" value="Subscribe!" th:value="#{subscribe.submit}"/>
th:alt-title
:将同时设置alt
和title
。
th:lang-xmllang
:将同时设置lang
和xml:lang
。
<img src="../../images/gtvglogo.png"
th:src="@{/images/gtvglogo.png}" th:alt-title="#{logo}" />
//等同于
<img src="../../images/gtvglogo.png"
th:src="@{/images/gtvglogo.png}" th:title="#{logo}" th:alt="#{logo}" />
5.5th:class、th:classappend、th:styleappend
th:class
:向元素添加class。
th:classappend
:向元素已有class附加 CSS 类。
th:styleappend
:向元素附加 style 样式。
<tr th:each="prod : ${prods}" class="row" th:classappend="${prodStat.odd}? 'odd'">
5.6th:checked
th:checked:固定值布尔属性。
<input type="checkbox" name="option2" checked /> <!-- HTML -->
<input type="checkbox" name="option1" checked="checked" /> <!-- XHTML -->
<input type="checkbox" name="active" th:checked="${user.active}" />
5.7th:each
5.7.1不带状态变量
${prods}:iterated expression 或 iterated variable。
prod:iteration variable 或 simply iter variable。
<tr th:each="prod : ${prods}">
<td th:text="${prod.name}">Onions</td>
<td th:text="${prod.price}">2.41</td>
<td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>
5.7.2带状态变量
状态变量在th:each
属性中定义并包含以下数据:
- 当前迭代索引,从 0 开始。这是
index
属性。 - 当前迭代索引,从 1 开始。这是
count
属性。 - 迭代变量中的元素总数。这是
size
属性。 - 每次迭代的 iter 变量。这是
current
属性。 - 当前迭代是偶数(even)还是奇数(odd)。这些是
even/odd
布尔属性。 - 当前迭代是否是第一个。这是
first
布尔属性。 - 当前迭代是否是最后一个。这是
last
布尔属性。
<table>
<tr>
<th>NAME</th>
<th>PRICE</th>
<th>IN STOCK</th>
</tr>
<tr th:each="prod,iterStat : ${prods}" th:class="${iterStat.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>
</tr>
</table>
//省略iterStat,使用prodStat
<table>
<tr>
<th>NAME</th>
<th>PRICE</th>
<th>IN STOCK</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>
</tr>
</table>
//odd只为奇数行建立 CSS 类
//都输出
<table>
<tr>
<th>NAME</th>
<th>PRICE</th>
<th>IN STOCK</th>
</tr>
<tr class="odd">
<td>Fresh Sweet Basil</td>
<td>4.99</td>
<td>yes</td>
</tr>
<tr>
<td>Italian Tomato</td>
<td>1.25</td>
<td>no</td>
</tr>
<tr class="odd">
<td>Yellow Bell Pepper</td>
<td>2.50</td>
<td>yes</td>
</tr>
<tr>
<td>Old Cheddar</td>
<td>18.75</td>
<td>yes</td>
</tr>
</table>
5.8th:if、th:unless、th:switch
th:if
属性不仅仅会计算布尔条件,支持的表达式如下:
- 如果值不为
null
:- 如果值是一个布尔值并且是
true
- 如果值是一个数字并且非零
- 如果值是一个字符并且非零
- 如果值是一个字符串并且不是
“false”
、“off”
或“no”
- 如果值不是布尔值、数字、字符或字符串。
- 如果值是一个布尔值并且是
- 如果值为
null
,th:if
将计算为false
。
<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"
th:href="@{/product/comments(prodId=${prod.id})}"
th:if="${not #lists.isEmpty(prod.comments)}">view</a>
</td>
</tr>
</table>
//输出
<table>
<tr>
<th>NAME</th>
<th>PRICE</th>
<th>IN STOCK</th>
<th>COMMENTS</th>
</tr>
<tr class="odd">
<td>Fresh Sweet Basil</td>
<td>4.99</td>
<td>yes</td>
<td>
<span>0</span> comment/s
</td>
</tr>
<tr>
<td>Italian Tomato</td>
<td>1.25</td>
<td>no</td>
<td>
<span>2</span> comment/s
<a href="/gtvg/product/comments?prodId=2">view</a>
</td>
</tr>
<tr class="odd">
<td>Yellow Bell Pepper</td>
<td>2.50</td>
<td>yes</td>
<td>
<span>0</span> comment/s
</td>
</tr>
<tr>
<td>Old Cheddar</td>
<td>18.75</td>
<td>yes</td>
<td>
<span>1</span> comment/s
<a href="/gtvg/product/comments?prodId=4">view</a>
</td>
</tr>
</table>
th:unless
:th:if
的逆属性。
<a href="comments.html"
th:href="@{/comments(prodId=${prod.id})}"
th:unless="${#lists.isEmpty(prod.comments)}">view</a>
th:switch
:等效于switch-case条件。
默认选项指定为th:case="*"
。
<div th:switch="${user.role}">
<p th:case="'admin'">User is an administrator</p>
<p th:case="#{roles.manager}">User is a manager</p>
<p th:case="*">User is some other thing</p>
</div>
5.9th:fragment、th:insert、th:replace、th:with、th:block
5.9.1不带参数的片段
th:fragment
:声明片段。
//footer.html
//创建片段
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
//th:fragment语法
<div th:fragment="copy">
© 2011 The Good Thymes Virtual Grocery
</div>
//id选择器语法
<div id="copy-section">
© 2011 The Good Thymes Virtual Grocery
</div>
</body>
</html>
<body>
...
//通过th:fragment引入片段
<div th:insert="~{footer :: copy}"></div>
//通过id选择器引入片段
<div th:insert="~{footer :: #copy-section}"></div>
</body>
th:insert
:引入片段作为元素的内容。
th:replace
:引入片段替代整个元素。
<footer th:fragment="copy">
© 2011 The Good Thymes Virtual Grocery
</footer>
<body>
...
<div th:insert="footer :: copy"></div>
<div th:replace="footer :: copy"></div>
</body>
//输出
<body>
...
<div>
<footer>
© 2011 The Good Thymes Virtual Grocery
</footer>
</div>
<footer>
© 2011 The Good Thymes Virtual Grocery
</footer>
</body>
5.9.2带参数的片段
th:with
:声明局部变量。
//显式参数
<div th:fragment="frag (onevar,twovar)">
<p th:text="${onevar} + ' - ' + ${twovar}">...</p>
</div>
//引入支持按位置和按命名传递参数
<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">
<p th:text="${onevar} + ' - ' + ${twovar}">...</p>
</div>
//引入只支持按命名传递参数
<div th:replace="::frag (onevar=${value1},twovar=${value2})">...</div>
<div th:replace="::frag (twovar=${value2},onevar=${value1})">...</div>
//按命名传递参数等同于th:replace 和 th:with 的组合
<div th:replace="::frag" th:with="onevar=${value1},twovar=${value2}">
5.9.3模板布局
th:block
:只是一个属性容器,它允许模板开发人员指定他们想要的任何属性。
//base.html
//创建片段
<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}">
<link rel="shortcut icon" th:href="@{/images/favicon.ico}">
<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}">
<link rel="stylesheet" th:href="@{/themes/smoothness/jquery-ui.css}">
</head>
//输出
<head>
<title>Awesome - Main</title>
<!-- Common styles and scripts -->
<link rel="stylesheet" type="text/css" media="all" href="/awe/css/awesomeapp.css">
<link rel="shortcut icon" href="/awe/images/favicon.ico">
<script type="text/javascript" src="/awe/sh/scripts/codebase.js"></script>
<link rel="stylesheet" href="/awe/css/bootstrap.min.css">
<link rel="stylesheet" href="/awe/themes/smoothness/jquery-ui.css">
</head>
5.9.4布局继承
<!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>
<!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>
5.10th:assert
th:assert
:指定一个逗号分隔的表达式列表,这些表达式应该被计算并为每次计算产生 true,如果不是则引发异常。
<div th:assert="${onevar},(${twovar} != 43)">...</div>
这对于验证片段签名中的参数非常有用。
<header th:fragment="contentheader(title)" th:assert="${!#strings.isEmpty(title)}">...</header>
6.表单
6.1<form>
<form action="#" th:action="@{/seedstartermng}" th:object="${seedStarter}" method="post">
...
</form>
6.2<input type=”text”>
<input type="text" th:field="*{datePlanted}" />
//等同于
<input type="text" id="datePlanted" name="datePlanted" th:value="*{datePlanted}" />
6.3<input type=”radio”>
<ul>
<li th:each="ty : ${allTypes}">
<input type="radio" th:field="*{type}" th:value="${ty}" />
<label th:for="${#ids.prev('type')}" th:text="#{${'seedstarter.type.' + ty}}">Wireframe</label>
</li>
</ul>
6.4<input type=”checkbox”>
//单值复选框
<div>
<label th:for="${#ids.next('covered')}" th:text="#{seedstarter.covered}">Covered</label>
<input type="checkbox" th:field="*{covered}" />
</div>
//多值复选框
<ul>
<li th:each="feat : ${allFeatures}">
<input type="checkbox" th:field="*{features}" th:value="${feat}" />
<label th:for="${#ids.prev('features')}"
th:text="#{${'seedstarter.feature.' + feat}}">Heating</label>
</li>
</ul>
//输出
<ul>
<li>
<input id="features1" name="features" type="checkbox" value="SEEDSTARTER_SPECIFIC_SUBSTRATE" />
<input name="_features" type="hidden" value="on" />
<label for="features1">Seed starter-specific substrate</label>
</li>
<li>
<input id="features2" name="features" type="checkbox" value="FERTILIZER" />
<input name="_features" type="hidden" value="on" />
<label for="features2">Fertilizer used</label>
</li>
<li>
<input id="features3" name="features" type="checkbox" value="PH_CORRECTOR" />
<input name="_features" type="hidden" value="on" />
<label for="features3">PH Corrector used</label>
</li>
</ul>
6.5<select>
<select th:field="*{type}">
<option th:each="type : ${allTypes}"
th:value="${type}"
th:text="#{${'seedstarter.type.' + type}}">Wireframe</option>
</select>
7.验证和错误消息
7.1字段错误
#fields.hasErrors()
:函数接收字段表达式作为参数 ( datePlanted
),并返回一个布尔值,告知该字段是否存在任何验证错误。
#fields.errors()
:函数接收字段表达式作为参数 ( datePlanted
),并返回该字段的所有错误。
//配置CSS类
<input type="text" th:field="*{datePlanted}"
th:class="${#fields.hasErrors('datePlanted')}? fieldError" />
//显示错误
<ul th:if="${#fields.hasErrors('datePlanted')}>
<li th:each="err : ${#fields.errors('datePlanted')}" th:text="${err}" />
</ul>
th:errorclass
:一个专门的属性,目的是简化上面的th:class
,可以将特定的 CSS 类设置给字段。
th:errors
:一个专门的属性,目的是简化上面的th:each
,它构建一个列表,其中包含指定选择器的所有错误,由<br />
分隔。
//配置CSS类
<input type="text" th:field="*{datePlanted}" class="small" th:errorclass="fieldError" />
//如果datePlanted有错误,将输出
<input type="text" id="datePlanted" name="datePlanted" value="2013-01-01" class="small fieldError" />
//显示错误
<p th:if="${#fields.hasErrors('datePlanted')}" th:errors="*{datePlanted}">Incorrect date</p>
7.2所有错误
#fields.hasErrors('*')
等价于#fields.hasErrors('all')
、#fields.hasAnyErrors()
。
#fields.errors('*')
等价于#fields.errors('all')
、#fields.allErrors()
。
//显示错误(th:each方式)
<ul th:if="${#fields.hasErrors('*')}">
<li th:each="err : ${#fields.errors('*')}" th:text="${err}">Input is incorrect</li>
</ul>
//显示错误(th:errors简化方式)
<p th:if="${#fields.hasErrors('all')}" th:errors="*{all}">Incorrect date</p>
7.3全局错误
Spring 表单中存在第三种类型的错误:全局错误。这些错误与表单中的任何特定字段都没有关联,但仍然存在。
#fields.hasErrors('global')
等价于#fields.hasGlobalErrors()
。
#fields.errors('global')
等价于#fields.globalErrors()
。
//显示错误(th:each方式)
<ul th:if="${#fields.hasErrors('global')}">
<li th:each="err : ${#fields.errors('global')}" th:text="${err}">Input is incorrect</li>
</ul>
//显示错误(th:errors简化方式)
<p th:if="${#fields.hasErrors('global')}" th:errors="*{global}">Incorrect date</p>
原创文章,作者:huoxiaoqiang,如若转载,请注明出处:https://www.huoxiaoqiang.com/java/springboot/16783.html