Demo下載,可以在這個demo上進行練習。
1.概述
SpringBoot支持的模板引擎:
-
Thymeleaf(SpringBoot推薦)
-
FreeMarker
-
Velocity
-
Groovy
-
JSP(以前開發Java Web時用的,現在不推薦)
前後端分離開發雖然已普遍存在,Thymeleaf也可以很好的進行前後臺的協作開發。
使用Thymeleaf 三大理由:
- 簡潔漂亮 容易理解
- 完美支持HTML5 使用瀏覽器直接打開頁面
- 不新增標籤 只需增強屬性
Thymeleaf支持處理六種模板:
- HTML(默認)
- XML
- TEXT
- JAVASCRIPT
- CSS
- RAW
2.添加Thymeleaf依賴
使用Thymeleaf需要在pom.xml文件中添加依賴:
<properties>
<thymeleaf.version>3.0.11.RELEASE</thymeleaf.version>
<thymeleaf-layout-dialect.version>2.4.1</thymeleaf-layout-dialect.version>
</properties>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
3.SpringBoot配置Thymeleaf
在application.yml或application.properties進行配置
# 是否開啓緩存,開發中設置成false,便於更改文件後,自動刷新
spring.thymeleaf.cache=false
# 檢查模板是否存在
spring.thymeleaf.check-template=true
# 是否啓用thymeleaf作爲視圖解析
spring.thymeleaf.enabled=true
# 是否spring el 表達式
spring.thymeleaf.enable-spring-el-compiler=false
# 模板文件編碼
spring.thymeleaf.encoding=UTF-8
# 指定不解析的視圖名以逗號分隔,
spring.thymeleaf.excluded-view-names=
# 解析的模板類型
spring.thymeleaf.mode=HTML
# 模板文件路徑前綴
spring.thymeleaf.prefix=classpath:/templates/
# 輸出類型
spring.thymeleaf.servlet.content-type=text/html
# 文件後綴
spring.thymeleaf.suffix=.html
#使用在thymeleaf使用消息表達式語法時#{xx},需指定文件名
spring.messages.basename=application
4.模板頁面
SpringBoot默認存放頁面模板的路徑在src/main/resources/templates
或者src/main/view/templates
,Thymeleaf默認的頁面文件後綴是.html。
Thymeleaf完美支持HTML5 使用瀏覽器直接打開頁面,因爲瀏覽器會忽略它不認識的屬性。絕大多數Standard Dialect處理器都是屬性處理器。Thymeleaf中很多屬性都是th:*
這種形式的,這種不是標準的HTML屬性,但是HTML5允許自定義以data-爲前綴的屬性,將其變成data-th-*
就會變成合法的屬性。
5.標準表達式語法
所有這些表達式都可以組合和嵌套。
5.1.簡單表達式
5.1.1.#{…}消息表達式
外部化文本就是從模板文件中提取的模板代碼片段,它們可以分開保存,典型的就是保存在.properties文件。因此它們可以被輕易地用其他語言的相應的文本來代替,這就是國際化的處理。外部化文本片段通常被稱爲“message”消息。消息都有一個key來識別它們。Thymeleaf允許我們用#{…}指定text對應的消息。消息表達式的消息會被抽取出來放單獨放在.properties文件中。消息表達式在國際化中最常用。國際化的例子可以參考:《SpringBoot+Thymeleaf實現國際化》
5.1.2.${…}變量表達式
在模板中從WebContext獲取請求參數和請求、會話、屬性:
${x}:返回一個存儲在Thymeleaf Context中的變量x,或者作爲一個請求屬性
${param.x}:返回一個請求參數x(可能是多個值的)
${session.x}:返回一個會話屬性x
${application.x}:返回一個servlet context的屬性x
舉例:
home.html
<!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="#{home.title}">Insert title here</title>
</head>
<body>
<div>
<a data-th-href="@{/locale(lang=zh_CN)}" th:text="#{home.language.chinese}">中文</a>
<a data-th-href="@{/locale(lang=en_US)}" th:text="#{home.language.english}">英語</a>
</div>
<h1 data-th-text="#{home.welcome(${userName})}">Fluid jumbotron</h1>
<h1 data-th-text="#{home.introduction(${userBean.name},${userBean.job},${userBean.hobby})}">Hello everyone!</h1>
<h1 data-th-text="${session.mark}">mark</h1>
<h1 data-th-text="${application.remark}">haha</h1>
</body>
</html>
HomeController.java
@Controller
public class HomeController{
@RequestMapping(value = {"/index","/home","/"},method = RequestMethod.GET)
public String getHomePage(Model model, Locale locale, HttpServletRequest request) {
// ${x}
model.addAttribute("userName","Tome");
// ${param.x}
UserBean userBean = new UserBean("Tome","IT Designer","play video game");
model.addAttribute("userBean",userBean);
// ${session.x}
HttpSession session=request.getSession();//獲取session並將mark存入session對象
session.setAttribute("mark", "歡迎大駕光臨!");
// ${application.x}
ServletContext application = request.getServletContext();//獲取ServletContext並將remark存入ServletContext對象
application.setAttribute("remark","ByeBye!");
return "home";
}
}
** th:text默認會轉義特殊字符,th:utext(unescaped text)則不會發生轉義,如 ** :
home.properties
home.general.introduction=Welcome to our <b>fantastic</b> grocery store!
home.html
// 發生轉義:Welcome to our <b>fantastic</b> grocery store!
<p data-th-text="#{home.general.introduction}">good</p>
// 不轉義(實際效果中fantastic會被加粗):Welcome to our fantastic grocery store!
<p data-th-utext="#{home.general.introduction}">good</p>
5.1.3.*{…}選擇變量表達式
選擇表達式就是表達式的結果使用th:object
屬性,如:
<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>
星號和美元符號語法也是可以混用的:
<div th:object="${session.user}">
<p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
<p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p>
<p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
</div>
當一個對象選擇所在位置,內部可以用#object來代表它:
<div th:object="${session.user}">
<p>Name: <span th:text="${#object.firstName}">Sebastian</span>.</p>
<p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p>
<p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
</div>
當沒有對象選擇時,美元符號和星號語法是等價的:
<div>
<p>Name: <span th:text="*{session.user.name}">Sebastian</span>.</p>
<p>Surname: <span th:text="*{session.user.surname}">Pepper</span>.</p>
<p>Nationality: <span th:text="*{session.user.nationality}">Saturn</span>.</p>
</div>
也就是說,如果沒有對象選擇被執行,那麼${…}與*{…}是等價的:
<div>
<p>Name: <span th:text="*{session.user.name}">Sebastian</span>.</p>
<p>Surname: <span th:text="*{session.user.surname}">Pepper</span>.</p>
<p>Nationality: <span th:text="*{session.user.nationality}">Saturn</span>.</p>
</div>
5.1.4.@{…}鏈接URL表達式
在web應該模板中,URL是很重要的。Thymeleaf專門有一個特殊的@{…}語法來處理它們。這個新語法將用在th:href
屬性上。
URL類型:
-
絕對URL:http://www.thymeleaf.org
-
相對URL:
(1)相對的頁面:user/login.html
(2)相對的上下文(在服務器的上下文名稱會被自動添加):/itemdetails?id=3
(3)相對的服務器(在同一個服務器上的另一個上下文(相當於另一個應用)裏,允許調用URL):~/billing/processInvoice
(4)協議相對URL: //code.jquery.com/query-2.3.0.min.js
這些URL表達式的真實處理和轉換都會由Thymeleaf模板引擎處理完成輸出。
舉例:
<!-- Will produce 'http://localhost:8080/gtvg/order/details?orderId=3' (plus rewriting) -->
<a href="details.html"
th:href="@{http://localhost:8080/gtvg/order/details(orderId=${o.id})}">view</a>
<!-- Will produce '/gtvg/order/details?orderId=3' (plus rewriting) -->
<a href="details.html" th:href="@{/order/details(orderId=${o.id})}">view</a>
<!-- Will produce '/gtvg/order/3/details' (plus rewriting) -->
<a href="details.html" th:href="@{/order/{orderId}/details(orderId=${o.id})}">view</a>
注意:
-
@{…}用在
th:href
上,將會替換掉<a>標籤的href屬性值 -
可以給表達式給URL添加參數,如
orderId=${o.id}
,如果有多參數,可以使用逗號隔開@{/order/process(execId=${execId},execType='FAST')}
-
變量模板也允許應用在URL上,如
@{/order/{orderId}/details(orderId=${orderId})}
-
以/開頭 (如: /order/details )的相對URL,將自動以應用程序上下文名稱作爲前綴。
-
如果cookie沒有啓用或未知,
;jsessionid=...
後綴可能會添加到相對URL,因此會話會被保留。這叫URL重寫。Thymeleaf允許插入自己的URL重寫,通過Servlet APIresponse.encodeURL(...)
機制。
URL也可以是其他表達式的計算值:
<a th:href="@{${url}(orderId=${o.id})}">view</a>
<a th:href="@{'/details/'+${user.login}(orderId=${o.id})}">view</a>
還有一個額外的語法用來創建server-root-relative(默認是context-root-relative)URL,目的是在同一服務器中,鏈接到不同的上下文。這樣的URL形如:@{~/path/to/something}
5.1.5.~{…}片段表達式
片段表達式可以讓我們將模板公共部分抽取出來作爲片段,然後在需要的地方引入。引入片段通過th:insert
或th:replace
來完成,其中th:include
在Thymeleaf3.0開始不再推薦使用,因此不做介紹。
<div th:insert="~{templateName :: fragmentName}"></div>
或
<div th:replace="~{templateName :: fragmentName}"></div>
包含模板templateName的標記fragmentName。也可以將整個模板templateName包含進來:
<div th:insert="~{templateName}"></div>
還可以插入當前模板裏的片段:
<div th:replace="~{this :: fragmentName}"></div>
或
<div th:replace="~{ :: fragmentName}"></div>
** 使用th:fragment="fragment"定義片段 **:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div data-th-fragment="copy">
© 2020 The W Grocery Store
</div>
</body>
</html>
除了這種方式外,還可以直接使用html的id,如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="copy-section">
© 2020 The W Grocery Store
</div>
</body>
</html>
如果用了這種方式,那麼~{…}表達式就要這樣寫:
<body>
...
<div th:insert="~{footer :: #copy-section}"></div>
</body>
6.常量
-
文字常量:如’one text’,‘another one’,…
-
數字常量:如1,34,12.3,…
-
布爾常量:true、false
<div data-th-if="${user.isAdmin()} == false">hello</div>
user對象的isAdmin()方法,
==false
寫在大花括號外,則由Thymeleaf處理,而寫成大花括號內,則將由OGNL/SpringEL引擎來處理,即:<div data-th-if="${user.isAdmin() == false}">hello</div>
-
Null常量:null
<div th:if="${variable.something} == null"> </div>
-
字面標記:如one、sometext,main,…
事實上數字、布爾常量、null都是字面標記的一種特殊形式,字面標記可以由字母、數字、方括號、點、連字符、下劃線組成,不能用空格、逗號。字面標記不需要任何綽號括住:
<div th:class="content">...</div>
7.文本操作
-
字符串連接:+
<span th:text="'The name of the user is ' + ${user.name}">
-
文本替換:|The name is {name}|
<span th:text="|Welcome to our application, ${user.name}!|">
等價於
<span th:text="'Welcome to our application, ' + ${user.name} + '!'">
用了文本替換,就可以不用字符串連接符+將字符串連接起來了,非常方便。但是請注意,只有變量/消息表達式${…},*{…},#{…}可以出現在文本替換|…|中,其他文字(’…’ )、布爾/數學標記,條件表達式等都不能出現在其中。
8.算術操作符
-
二元操作符:+,-,*,/,%
<div th:with="isEven=(${prodStat.count} % 2 == 0)">
以上操作將是Thymeleaf標準表達式引擎處理,如果
% 2 == 0
出現在大花括號內,則由OGNL/SpringEL引擎來處理:<div th:with="isEven=${prodStat.count % 2 == 0}">
-
一元操作符:-
9.比較和相等
-
比較:>,<,>=,<= (gt,lt,ge,le)
因爲<,>是html標記的組成部分,不允許出現在屬性部分,因此用> <
<div th:if="${prodStat.count} > 1"> <span th:text="'Execution mode is ' + ( (${execMode} == 'dev')? 'Development' : 'Production')">
-
相等:==,!= (eq,ne)
10.條件操作符
-
if-then:(if) ? (then)
<tr data-th-each="product,dd : ${products}" data-th-class="${dd.odd}? 'odd'">
-
if-then-else:(if) ? (then) : (else)
條件表達式,這三部分可以是變量(${,},*{…}),消息(#{…}),URL(@{…})或文本(’…’)。
<tr th:class="${row.even}? 'even' : 'odd'"> ... </tr>
條件表達式也可以嵌套:
<tr th:class="${row.even}? (${row.first}? 'first' : 'even') : 'odd'"> ... </tr>
甚至else部分也可省略,這種情況下,如果條件爲false就返回:
<tr th:class="${row.even}? 'alt'"> ... </tr>
-
default:(value) ?: (defaultvalue)
默認表達式是一種特殊的條件表達式,當value是null時,就使用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>
還可在默認值部分嵌套其他條件表達式:
<p> Name: <span th:text="*{firstName}?: (*{admin}? 'Admin' : #{default.username})">Sebastian</span> </p>
11.特殊標記
- 無操作:_
不做任何操作
<span th:text="${user.name} ?: _">no user authenticated</span>
等價於
<span th:text="${user.name} ?: 'no user authenticated'">...</span>
12.表達式基本對象
在解釋模板時,會生成一些對象,在表達式中使用會使得表達式更加靈活。這些對象使用#號來引用:
-
#ctx:上下文對象
/* * ====================================================================== * See javadoc API for class org.thymeleaf.context.IContext * ====================================================================== */ ${#ctx.locale} ${#ctx.variableNames} /* * ====================================================================== * See javadoc API for class org.thymeleaf.context.IWebContext * ====================================================================== */ ${#ctx.request} ${#ctx.response} ${#ctx.session} ${#ctx.servletContext}
-
#vars 和 #root:與#ctx都是同一個對象,但推薦使用#ctx
-
#locale:上下文區域,直接訪問關聯當前請求的java.util.Locale對象
${#locale}
-
Web上下文對象
-
#request:直接訪問關聯當前請求的javax.servlet.http.HttpServletRequest對象
${#request.getAttribute('foo')} ${#request.getParameter('foo')} ${#request.getContextPath()} ${#request.getRequestName()} ...
-
#session : 直接訪問關聯當前請求的javax.servlet.http.HttpSession對象
${#session.getAttribute('foo')} ${#session.id} ${#session.lastAccessedTime} ...
-
#servletContext : 直接訪問關聯當前請求的javax.servlet.ServletContext對象
${#servletContext.getAttribute('foo')} ${#servletContext.contextPath} ...
-
-
爲Web上下文命令空間提供的request/session屬性
- param:獲取請求參數
/* * ============================================================================ * See javadoc API for class org.thymeleaf.context.WebRequestParamsVariablesMap * ============================================================================ */ ${param.foo} // Retrieves a String[] with the values of request parameter 'foo' ${param.size()} ${param.isEmpty()} ${param.containsKey('foo')} ...
- session:獲取會話屬性
/* * ====================================================================== * See javadoc API for class org.thymeleaf.context.WebSessionVariablesMap * ====================================================================== */ ${session.foo} ${session.size()} ${session.isEmpty()} ${session.containsKey('foo')} ...
- application:獲取application/servlet上下文屬性
/*
* =============================================================================
* See javadoc API for class org.thymeleaf.context.WebServletContextVariablesMap
* =============================================================================
*/
${application.foo}
// Retrieves the ServletContext atttribute 'foo'
${application.size()}
${application.isEmpty()}
${application.containsKey('foo')}
...
13.表達式工具類對象
Thymeleaf提供很多工具類對象,幫助我們在表達式中完成一些常見的任務。
-
#execInfo : 提供在Thymeleaf標準表達式中被處理的模板的相關信息的表達式對象
/* * Return the name and mode of the 'leaf' template. This means the template * from where the events being processed were parsed. So if this piece of * code is not in the root template "A" but on a fragment being inserted * into "A" from another template called "B", this will return "B" as a * name, and B's mode as template mode. */ ${#execInfo.templateName} ${#execInfo.templateMode} /* * Return the name and mode of the 'root' template. This means the template * that the template engine was originally asked to process. So if this * piece of code is not in the root template "A" but on a fragment being * inserted into "A" from another template called "B", this will still * return "A" and A's template mode. */ ${#execInfo.processedTemplateName} ${#execInfo.processedTemplateMode} /* * Return the stacks (actually, List<String> or List<TemplateMode>) of * templates being processed. The first element will be the * 'processedTemplate' (the root one), the last one will be the 'leaf' * template, and in the middle all the fragments inserted in nested * manner to reach the leaf from the root will appear. */ ${#execInfo.templateNames} ${#execInfo.templateModes} /* * Return the stack of templates being processed similarly (and in the * same order) to 'templateNames' and 'templateModes', but returning * a List<TemplateData> with the full template metadata. */ ${#execInfo.templateStack}
-
#messages:提供獲取外部化消息的方法
/* * Obtain externalized messages. Can receive a single key, a key plus arguments, * or an array/list/set of keys (in which case it will return an array/list/set of * externalized messages). * If a message is not found, a default message (like '??msgKey??') is returned. */ ${#messages.msg('msgKey')} ${#messages.msg('msgKey', param1)} ${#messages.msg('msgKey', param1, param2)} ${#messages.msg('msgKey', param1, param2, param3)} ${#messages.msgWithParams('msgKey', new Object[] {param1, param2, param3, param4})} ${#messages.arrayMsg(messageKeyArray)} ${#messages.listMsg(messageKeyList)} ${#messages.setMsg(messageKeySet)} /* * Obtain externalized messages or null. Null is returned instead of a default * message if a message for the specified key is not found. */ ${#messages.msgOrNull('msgKey')} ${#messages.msgOrNull('msgKey', param1)} ${#messages.msgOrNull('msgKey', param1, param2)} ${#messages.msgOrNull('msgKey', param1, param2, param3)} ${#messages.msgOrNullWithParams('msgKey', new Object[] {param1, param2, param3, param4})} ${#messages.arrayMsgOrNull(messageKeyArray)} ${#messages.listMsgOrNull(messageKeyList)} ${#messages.setMsgOrNull(messageKeySet)}
-
#uris:爲執行URI/URL操作(轉義/不轉義)提供的工具對象
/* * Escape/Unescape as a URI/URL path */ ${#uris.escapePath(uri)} ${#uris.escapePath(uri, encoding)} ${#uris.unescapePath(uri)} ${#uris.unescapePath(uri, encoding)} /* * Escape/Unescape as a URI/URL path segment (between '/' symbols) */ ${#uris.escapePathSegment(uri)} ${#uris.escapePathSegment(uri, encoding)} ${#uris.unescapePathSegment(uri)} ${#uris.unescapePathSegment(uri, encoding)} /* * Escape/Unescape as a Fragment Identifier (#frag) */ ${#uris.escapeFragmentId(uri)} ${#uris.escapeFragmentId(uri, encoding)} ${#uris.unescapeFragmentId(uri)} ${#uris.unescapeFragmentId(uri, encoding)} /* * Escape/Unescape as a Query Parameter (?var=value) */ ${#uris.escapeQueryParam(uri)} ${#uris.escapeQueryParam(uri, encoding)} ${#uris.unescapeQueryParam(uri)} ${#uris.unescapeQueryParam(uri, encoding)}
-
#conversions:允許轉換服務在模板任意地方執行的工具對象
/* * Execute the desired conversion of the 'object' value into the * specified class. */ ${#conversions.convert(object, 'java.util.TimeZone')} ${#conversions.convert(object, targetClass)}
-
#dates:提供java.util.Date相關的工具對象
/* * Format date with the standard locale format * Also works with arrays, lists or sets */ ${#dates.format(date)} ${#dates.arrayFormat(datesArray)} ${#dates.listFormat(datesList)} ${#dates.setFormat(datesSet)} /* * Format date with the ISO8601 format * Also works with arrays, lists or sets */ ${#dates.formatISO(date)} ${#dates.arrayFormatISO(datesArray)} ${#dates.listFormatISO(datesList)} ${#dates.setFormatISO(datesSet)} /* * Format date with the specified pattern * Also works with arrays, lists or sets */ ${#dates.format(date, 'dd/MMM/yyyy HH:mm')} ${#dates.arrayFormat(datesArray, 'dd/MMM/yyyy HH:mm')} ${#dates.listFormat(datesList, 'dd/MMM/yyyy HH:mm')} ${#dates.setFormat(datesSet, 'dd/MMM/yyyy HH:mm')} /* * Obtain date properties * Also works with arrays, lists or sets */ ${#dates.day(date)} ${#dates.month(date)} ${#dates.monthName(date)} ${#dates.monthNameShort(date)} ${#dates.year(date)} ${#dates.dayOfWeek(date)} ${#dates.dayOfWeekName(date)} ${#dates.dayOfWeekNameShort(date)} ${#dates.hour(date)} ${#dates.minute(date)} ${#dates.second(date)} ${#dates.millisecond(date)} /* * Create date (java.util.Date) objects from its components */ ${#dates.create(year,month,day)} ${#dates.create(year,month,day,hour,minute)} ${#dates.create(year,month,day,hour,minute,second)} ${#dates.create(year,month,day,hour,minute,second,millisecond)} /* * Create a date (java.util.Date) object for the current date and time */ ${#dates.createNow()} ${#dates.createNowForTimeZone()} /* * Create a date (java.util.Date) object for the current date (time set to 00:00) */ ${#dates.createToday()} ${#dates.createTodayForTimeZone()} /* * Create a date (java.util.Date) object for the current date (time set to 00:00) */ ${#dates.createToday()} ${#dates.createTodayForTimeZone()}
-
#calendars:類似#dates
/* * Format calendar with the standard locale format * Also works with arrays, lists or sets */ ${#calendars.format(cal)} ${#calendars.arrayFormat(calArray)} ${#calendars.listFormat(calList)} ${#calendars.setFormat(calSet)} /* * Format calendar with the ISO8601 format * Also works with arrays, lists or sets */ ${#calendars.formatISO(cal)} ${#calendars.arrayFormatISO(calArray)} ${#calendars.listFormatISO(calList)} ${#calendars.setFormatISO(calSet)} /* * Format calendar with the specified pattern * Also works with arrays, lists or sets */ ${#calendars.format(cal, 'dd/MMM/yyyy HH:mm')} ${#calendars.arrayFormat(calArray, 'dd/MMM/yyyy HH:mm')} ${#calendars.listFormat(calList, 'dd/MMM/yyyy HH:mm')} ${#calendars.setFormat(calSet, 'dd/MMM/yyyy HH:mm')} /* * Obtain calendar properties * Also works with arrays, lists or sets */ ${#calendars.day(date)} // ${#calendars.month(date)} // ${#calendars.monthName(date)} // ${#calendars.monthNameShort(date)} // ${#calendars.year(date)} // ${#calendars.dayOfWeek(date)} // ${#calendars.dayOfWeekName(date)} // ${#calendars.dayOfWeekNameShort(date)} // ${#calendars.hour(date)} // ${#calendars.minute(date)} // ${#calendars.second(date)} // ${#calendars.millisecond(date)} /* * Create calendar (java.util.Calendar) objects from its components */ ${#calendars.create(year,month,day)} ${#calendars.create(year,month,day,hour,minute)} ${#calendars.create(year,month,day,hour,minute,second)} ${#calendars.create(year,month,day,hour,minute,second,millisecond)} ${#calendars.createForTimeZone(year,month,day,timeZone)} ${#calendars.createForTimeZone(year,month,day,hour,minute,timeZone)} ${#calendars.createForTimeZone(year,month,day,hour,minute,second,timeZone)} ${#calendars.createForTimeZone(year,month,day,hour,minute,second,millisecond,timeZone)} /* * Create a calendar (java.util.Calendar) object for the current date and time */ ${#calendars.createNow()} ${#calendars.createNowForTimeZone()} /* * Create a calendar (java.util.Calendar) object for the current date (time set to 00:00) */ ${#calendars.createToday()} ${#calendars.createTodayForTimeZone()}
-
#numbers:數字對象的工具類
/* * ========================== * Formatting integer numbers * ========================== */ /* * Set minimum integer digits. * Also works with arrays, lists or sets */ ${#numbers.formatInteger(num,3)} ${#numbers.arrayFormatInteger(numArray,3)} ${#numbers.listFormatInteger(numList,3)} ${#numbers.setFormatInteger(numSet,3)} /* * Set minimum integer digits and thousands separator: * 'POINT', 'COMMA', 'WHITESPACE', 'NONE' or 'DEFAULT' (by locale). * Also works with arrays, lists or sets */ ${#numbers.formatInteger(num,3,'POINT')} ${#numbers.arrayFormatInteger(numArray,3,'POINT')} ${#numbers.listFormatInteger(numList,3,'POINT')} ${#numbers.setFormatInteger(numSet,3,'POINT')} /* * ========================== * Formatting decimal numbers * ========================== */ /* * Set minimum integer digits and (exact) decimal digits. * Also works with arrays, lists or sets */ ${#numbers.formatDecimal(num,3,2)} ${#numbers.arrayFormatDecimal(numArray,3,2)} ${#numbers.listFormatDecimal(numList,3,2)} ${#numbers.setFormatDecimal(numSet,3,2)} /* * Set minimum integer digits and (exact) decimal digits, and also decimal separator. * Also works with arrays, lists or sets */ ${#numbers.formatDecimal(num,3,2,'COMMA')} ${#numbers.arrayFormatDecimal(numArray,3,2,'COMMA')} ${#numbers.listFormatDecimal(numList,3,2,'COMMA')} ${#numbers.setFormatDecimal(numSet,3,2,'COMMA')} /* * Set minimum integer digits and (exact) decimal digits, and also thousands and * decimal separator. * Also works with arrays, lists or sets */ ${#numbers.formatDecimal(num,3,'POINT',2,'COMMA')} ${#numbers.arrayFormatDecimal(numArray,3,'POINT',2,'COMMA')} ${#numbers.listFormatDecimal(numList,3,'POINT',2,'COMMA')} ${#numbers.setFormatDecimal(numSet,3,'POINT',2,'COMMA')} /* * ===================== * Formatting currencies * ===================== */ ${#numbers.formatCurrency(num)} ${#numbers.arrayFormatCurrency(numArray)} ${#numbers.listFormatCurrency(numList)} ${#numbers.setFormatCurrency(numSet)} /* * ====================== * Formatting percentages * ====================== */ ${#numbers.formatPercent(num)} ${#numbers.arrayFormatPercent(numArray)} ${#numbers.listFormatPercent(numList)} ${#numbers.setFormatPercent(numSet)} /* * Set minimum integer digits and (exact) decimal digits. */ ${#numbers.formatPercent(num, 3, 2)} ${#numbers.arrayFormatPercent(numArray, 3, 2)} ${#numbers.listFormatPercent(numList, 3, 2)} ${#numbers.setFormatPercent(numSet, 3, 2)} /* * =============== * Utility methods * =============== */ /* * Create a sequence (array) of integer numbers going * from x to y */ ${#numbers.sequence(from,to)} ${#numbers.sequence(from,to,step)}
-
#strings :String對象的工具類
/* * Null-safe toString() */ ${#strings.toString(obj)} // also array*, list* and set* /* * Check whether a String is empty (or null). Performs a trim() operation before check * Also works with arrays, lists or sets */ ${#strings.isEmpty(name)} ${#strings.arrayIsEmpty(nameArr)} ${#strings.listIsEmpty(nameList)} ${#strings.setIsEmpty(nameSet)} /* * Perform an 'isEmpty()' check on a string and return it if false, defaulting to * another specified string if true. * Also works with arrays, lists or sets */ ${#strings.defaultString(text,default)} ${#strings.arrayDefaultString(textArr,default)} ${#strings.listDefaultString(textList,default)} ${#strings.setDefaultString(textSet,default)} /* * Check whether a fragment is contained in a String * Also works with arrays, lists or sets */ ${#strings.contains(name,'ez')} ${#strings.containsIgnoreCase(name,'ez')} /* * Check whether a String starts or ends with a fragment * Also works with arrays, lists or sets */ ${#strings.startsWith(name,'Don')} ${#strings.endsWith(name,endingFragment)} /* * Substring-related operations * Also works with arrays, lists or sets */ ${#strings.indexOf(name,frag)} ${#strings.substring(name,3,5)} ${#strings.substringAfter(name,prefix)} ${#strings.substringBefore(name,suffix)} ${#strings.replace(name,'las','ler')} /* * Append and prepend * Also works with arrays, lists or sets */ ${#strings.prepend(str,prefix)} ${#strings.append(str,suffix)} // also array*, list* and set* // also array*, list* and set* /* * Change case * Also works with arrays, lists or sets */ ${#strings.toUpperCase(name)} ${#strings.toLowerCase(name)} // also array*, list* and set* // also array*, list* and set* /* * Split and join */ ${#strings.arrayJoin(namesArray,',')} ${#strings.listJoin(namesList,',')} ${#strings.setJoin(namesSet,',')} ${#strings.arraySplit(namesStr,',')} ${#strings.listSplit(namesStr,',')} ${#strings.setSplit(namesStr,',')} // returns String[] // returns List<String> // returns Set<String> /* * Trim * Also works with arrays, lists or sets */ ${#strings.trim(str)} // also array*, list* and set* /* * Compute length * Also works with arrays, lists or sets */ ${#strings.length(str)} // also array*, list* and set* /* * Abbreviate text making it have a maximum size of n. If text is bigger, it * will be clipped and finished in "..." * Also works with arrays, lists or sets */ ${#strings.abbreviate(str,10)} // also array*, list* and set* /* * Convert the first character to upper-case (and vice-versa) */ ${#strings.capitalize(str)} // also array*, list* and set* ${#strings.unCapitalize(str)} // also array*, list* and set* /* * Convert the first character of every word to upper-case */ ${#strings.capitalizeWords(str)} // also array*, list* and set* ${#strings.capitalizeWords(str,delimiters)} // also array*, list* and set* /* * Escape the string */ ${#strings.escapeXml(str)} ${#strings.escapeJava(str)} ${#strings.escapeJavaScript(str)} ${#strings.escapeJavaScript(str)} ${#strings.unescapeJava(str)} ${#strings.unescapeJavaScript(str)} /* * Null-safe comparison and concatenation */ ${#strings.equals(first, second)} ${#strings.equalsIgnoreCase(first, second)} ${#strings.concat(values...)} ${#strings.concatReplaceNulls(nullValue, values...)} /* * Random */ ${#strings.randomAlphanumeric(count)}
-
#objects:對象的工具類
/* * Return obj if it is not null, and default otherwise * Also works with arrays, lists or sets */ ${#objects.nullSafe(obj,default)} ${#objects.arrayNullSafe(objArray,default)} ${#objects.listNullSafe(objList,default)} ${#objects.setNullSafe(objSet,default)}
-
#bools:布爾計算的工具類
/* * Evaluate a condition in the same way that it would be evaluated in a th:if tag * (see conditional evaluation chapter afterwards). * Also works with arrays, lists or sets */ ${#bools.isTrue(obj)} ${#bools.arrayIsTrue(objArray)} ${#bools.listIsTrue(objList)} ${#bools.setIsTrue(objSet)} /* * Evaluate with negation * Also works with arrays, lists or sets */ ${#bools.isFalse(cond)} ${#bools.arrayIsFalse(condArray)} ${#bools.listIsFalse(condList)} ${#bools.setIsFalse(condSet)} /* * Evaluate and apply AND operator * Receive an array, a list or a set as parameter */ ${#bools.arrayAnd(condArray)} ${#bools.listAnd(condList)} ${#bools.setAnd(condSet)} /* * Evaluate and apply OR operator * Receive an array, a list or a set as parameter */ ${#bools.arrayOr(condArray)} ${#bools.listOr(condList)} ${#bools.setOr(condSet)}
-
#arrays:數組工具類
/* * Converts to array, trying to infer array component class. * Note that if resulting array is empty, or if the elements * of the target object are not all of the same class, * this method will return Object[]. */ ${#arrays.toArray(object)} /* * Convert to arrays of the specified component class. */ ${#arrays.toStringArray(object)} ${#arrays.toIntegerArray(object)} ${#arrays.toLongArray(object)} ${#arrays.toDoubleArray(object)} ${#arrays.toFloatArray(object)} ${#arrays.toBooleanArray(object)} /* * Compute length */ ${#arrays.length(array)} /* * Check whether array is empty */ ${#arrays.isEmpty(array)} /* * Check if element or elements are contained in array */ ${#arrays.contains(array, element)} ${#arrays.containsAll(array, elements)}
-
#lists:列表的工具類
/* * Converts to list */ ${#lists.toList(object)} /* * Compute size */ ${#lists.size(list)} /* * Check whether list is empty */ ${#lists.isEmpty(list)} /* * Check if element or elements are contained in list */ ${#lists.contains(list, element)} ${#lists.containsAll(list, elements)} /* * Sort a copy of the given list. The members of the list must implement * comparable or you must define a comparator. */ ${#lists.sort(list)} ${#lists.sort(list, comparator)}
-
#sets:集合工具類
/* * Converts to set */ ${#sets.toSet(object)} /* * Compute size */ ${#sets.size(set)} /* * Check whether set is empty */ ${#sets.isEmpty(set)} /* * Check if element or elements are contained in set */ ${#sets.contains(set, element)} ${#sets.containsAll(set, elements)}
-
#maps:映射工具類
/* * Compute size */ ${#maps.size(map)} /* * Check whether map is empty */ ${#maps.isEmpty(map)} /* * Check if key/s or value/s are contained in maps */ ${#maps.containsKey(map, key)} ${#maps.containsAllKeys(map, keys)} ${#maps.containsValue(map, value)} ${#maps.containsAllValues(map, value)}
-
#aggregates:對數組和集合進行的統計的工具類
/* * Compute sum. Returns null if array or collection is empty */ ${#aggregates.sum(array)} ${#aggregates.sum(collection)} /* * Compute average. Returns null if array or collection is empty */ ${#aggregates.avg(array)} ${#aggregates.avg(collection)}
-
#ids:處理id屬性的工具類
/* * Normally used in th:id attributes, for appending a counter to the id attribute value * so that it remains unique even when involved in an iteration process. */ ${#ids.seq('someId')} /* * Normally used in th:for attributes in <label> tags, so that these labels can refer to Ids * generated by means if the #ids.seq(...) function. * * Depending on whether the <label> goes before or after the element with the #ids.seq(...) * function, the "next" (label goes before "seq") or the "prev" function (label goes after * "seq") function should be called. */ ${#ids.next('someId')} ${#ids.prev('someId')}
舉例:
<p>
Today is: <span th:text="${#calendars.format(today,'dd MMMM yyyy')}">13 May 2011</span>
</p>
HomeController.java
@Controller
public class HomeController{
@RequestMapping(value = {"/index","/home","/"},method = RequestMethod.GET)
public String getHomePage(Model model, Locale locale, HttpServletRequest request) {
model.addAttribute("today",new Date());
return "home";
}
}
14.數據轉換和格式化
Thymeleaf爲變量表達式({{…}},*{{…}},允許我們使用配置好的轉換服務對數據進行轉換。基本形式:
<td th:text="${{user.lastAccessDate}}">...</td>
15.給任意屬性設置值
很可惜,這種給屬性值設置值的方式,在模板中並不常用。這種方式通過th:attr就能夠改變標記的屬性的值:
<form action="subscribe.html" data-th-attr="action=@{/subscribe}">
<fieldset>
<input type="text" name="email"/>
<input type="submit" value="Subscribe!" data-th-attr="value=#{subscribe.submit}">
</fieldset>
</form>
th:attr用逗號分隔開每個屬性:
<img src="../../images/gtvglogo.png"
th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />
最後會輸出:
<img src="/gtgv/images/gtvglogo.png" title="Logo de Good Thymes" alt="Logo de Good Thymes" />
16.給具體屬性設置值th:*
如設置標記的value屬性:
<input type="submit" value="Subscribe!" th:value="#{subscribe.submit}"/>
再如form標記的action屬性:
<form action="subscribe.html" th:action="@{/subscribe}">
還有更多,請參考Thymeleaf官方的相關文檔。
17.迭代
17.1.th:each迭代屬性
舉例
ProductListController.java
@Controller
public class ProductListController {
@RequestMapping(value = {"product/list"},method = RequestMethod.GET)
public String showProducts(Model model, Locale locale, HttpServletRequest request) {
ProductService productService = new ProductService();
List<Product> products = productService.findAll();
model.addAttribute("products",products);
return "product/list";
}
}
product/list.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<link rel="stylesheet" type="text/css" media="all" data-th-href="@{/css/wgrocery.css}" />
<title>Title</title>
</head>
<body>
<h1>Product List</h1>
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
<th>In Stock</th>
<th>Comments</th>
</tr>
</thead>
<tbody data-th-remove="all-but-first">
<!--`product : ${products}`意思是爲 ${products}中的每一個元素重複這個模板片段,並通過product變量使用當前元素。-->
<tr data-th-each="product : ${products}" data-th-class="${productStat.odd}? 'odd'">
<td data-th-text="${product.name}"></td>
<td data-th-text="${product.price}"></td>
<td data-th-text="${product.inStock} ? #{true} : ${false}"></td>
<td>
<span data-th-text="${#lists.size(product.comments)}">0</span> comment/s
<a href="comments.html" data-th-href="@{/product/comments(prodId=${product.id})}"
data-th-unless="${#lists.isEmpty(product.comments)}">view</a>
</td>
</tr>
</tbody>
</table>
<p>
<a href="../home.html" th:href="@{/home}">Return to home</a>
</p>
</body>
</html>
我們在list.html使用th:each迭代products列表。product : ${products}
意思是爲 ${products}中的每一個元素重複這個模板片段,並通過product變量使用當前元素。
- ${products}:我們稱其爲被迭代表達式或被迭代變量
- product:我們稱其爲迭代變量或中間變量
### 17.2什麼對象可以被迭代呢?
- 任何實現了java.util.Iterable的對象
- 任何實現了java.util.Enumeration的對象
- 任何實現了java.util.Iterator的對象
- 任何實現了java.util.Map的對象
- 任何數組
17.3獲取迭代狀態
當使用th:each
時,Thymeleaf提供一些狀態變量跟蹤迭代的狀態。這些狀態變量在th:each
屬性中定義的,包括以下這一些:
- index:當前迭代的索引,從0開始
- count:當前迭代的索引,從1開始
- size:被迭代變量中元素總數
- current:每次迭代的迭代變量
- even/odd:檢查當前迭代是雙數還是單數,返回true或false
- first:檢查當前迭代是否是第一個
- last:檢查當前迭代是否是最後一個
17.3.1如何獲得這些狀態變量呢?
<tr data-th-each="product,dd : ${products}" data-th-class="${dd.odd}? 'odd'">...</tr>
狀態變量是定義在th:each屬性當中的,跟在迭代變量後,用逗號分開,如上面的dd就是狀態變量。事實上,如果我們不顯示定義我們的狀態變量,Thymeleaf也會幫我們創建一個,它的名稱是迭代變量+Stat後綴
,如下所示:
<tr data-th-each="product : ${products}" data-th-class="${productStat.odd}? 'odd'">...</tr>
上面沒有顯示定義狀態變量,所以默認的狀態變量是productStat。
18.數據懶加載
當數據需要時,再加載。爲了支持懶加載,Thymeleaf提供了懶加載上下文變量,上下文變量實現了ILazyContextVariable接口。在大多數情況下,都直接繼承LazyContextVariable的默認實現。舉個例子:
ProductListController.java
@Controller
public class ProductListController {
@RequestMapping(value = {"product/list"},method = RequestMethod.GET)
public String showProducts(Model model, boolean show) {
ProductService productService = new ProductService();
// 數據懶加載
model.addAttribute("products", new LazyContextVariable<List<Product>>() {
@Override
protected List<Product> loadValue() {
return productService.findAll();
}
});
model.addAttribute("show",show);
return "product/list";
}
}
list.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<link rel="stylesheet" type="text/css" media="all" data-th-href="@{/css/wgrocery.css}" />
<title>Title</title>
</head>
<body>
<!-- 如果是true,則進行入執行-->
<table data-th-if="${show}">
<thead>
<tr>
<th>Name</th>
<th>Price</th>
<th>In Stock</th>
<th>Comments</th>
</tr>
</thead>
<tbody data-th-remove="all-but-first">
<tr data-th-each="product : ${products}" data-th-class="${productStat.odd}? 'odd'">
<td data-th-text="${product.name}"></td>
<td data-th-text="${product.price}"></td>
<td data-th-text="${product.inStock} ? #{true} : ${false}"></td>
<td>
<span data-th-text="${#lists.size(product.comments)}">0</span> comment/s
<a href="comments.html" data-th-href="@{/product/comments(prodId=${product.id})}"
data-th-unless="${#lists.isEmpty(product.comments)}">view</a>
</td>
</tr>
</tbody>
</table>
</body>
</html>
- http://localhost:8080/product/list?show=true:加載數據
- http://localhost:8080/product/list?show=false:不加載數據
如果Controller裏,沒有使用如下懶加載:
// 懶加載
model.addAttribute("products", new LazyContextVariable<List<Product>>() {
@Override
protected List<Product> loadValue() {
return productService.findAll();
}
});
那麼無論是http://localhost:8080/product/list?show=true
還是http://localhost:8080/product/list?show=false
都會觸發數據加載,區別僅在於有沒有顯示出來而已。
19.條件屬性th:if和th:unless
有時模板裏的部分代碼,你只想在符合某種條件下時才顯示。如上面的產品列表裏,如果評論數據不爲0時,則顯示view按鈕:
<a href="comments.html" data-th-href="@{/product/comments(prodId=${product.id})}"
data-th-if="not ${#lists.isEmpty(product.comments)}">view</a>
th:if屬性的表達式的值爲true的情況,遵循以下規則:
-
如果值爲非null,表達式的值就是true
- 如果值是布爾類型,並且值是true
- 如果值是數字類型,並且值是非0的情況
- 如果值是字符類型,並且值是非0的情況
- 如果值是字符串類型,並且值不是“false”、“off”、“no”的情況
- 如果值不布爾值、數字值、字符值、字符串值的情況
-
如果值是null,那麼th:if的值是false
th:if有一個相反的屬性th:unless:
<a href="comments.html" data-th-href="@{/product/comments(prodId=${product.id})}"
data-th-if="not ${#lists.isEmpty(product.comments)}">view</a>
等價於
<a href="comments.html" data-th-href="@{/product/comments(prodId=${product.id})}"
data-th-unless="${#lists.isEmpty(product.comments)}">view</a>
20.switch聲明th:switch和th: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>
21.模板佈局
21.1模板片段
將模板中的公共部分抽取出來單獨放在一個html中,然後在模板中引用。這種方式有利用我們複用代碼、修改代碼、維護代碼。如將模板網頁的header或footer、menus抽取出來,可以這樣做使用th:fragment屬性定義片段:
-
定義片段
th:fragment
片段也是一個完整的html。下面我們在footer.html裏定義了一個片段copy
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div data-th-fragment="copy"> $copy; 2020 The W Grocery Store </div> </body> </html>
-
在模板中包含這些片段
在模板中使用
th:insert
和th:replace
(th:include
也可以,但在Thymeleaf3.0開始不再推薦使用)我們在模板list.html使用copy片段:
<body> ... <div th:insert="~{footer :: copy}"></div> </body>
等價於
<body> ... <div th:replace="~{footer :: copy}"></div> </body>
等價於
<body> ... <div th:insert="footer :: copy"></div> </body>
等價於
<body> ... <div th:replace="footer :: copy"></div> </body>
th:insert
、th:replace
、th:include
的區別:
有以下這樣一個片段:
<footer th:fragment="copy">
© 2011 The Good Thymes Virtual Grocery
</footer>
分別用三種方式引入:
<body>
...
<div th:insert="footer :: copy"></div>
<div th:replace="footer :: copy"></div>
<div th:include="footer :: copy"></div>
</body>
區別:
<body>
...
<div>
<footer>
© 2011 The Good Thymes Virtual Grocery
</footer>
</div>
<footer>
© 2011 The Good Thymes Virtual Grocery
</footer>
<div>
© 2011 The Good Thymes Virtual Grocery
</div>
</body>
21.2.參數化片段簽名
讓片段有類似函數的機制。定義參數化片段:
<div th:fragment="frag (onevar,twovar)">
<p th:text="${onevar} + ' - ' + ${twovar}">...</p>
</div>
使用th:insert
或th:replace
調用參數化片段
<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})">
等價於th:replace
和 th:with
的組合
<div th:replace="::frag" th:with="onevar=${value1},twovar=${value2}">
21.3模板內的斷言
斷言屬性th:assert
可以指定一個以逗號分隔的表達式列表,列表中的表達式必須都爲true,否則就會報異常:
<div th:assert="${onevar},(${twovar} != 43)">...</div>
這個屬性可以很方便驗證片段簽名的參數:
<header th:fragment="contentheader(title)" th:assert="${!#strings.isEmpty(title)}">...</header>
21.4靈活的佈局
我們可以指定參數,這個參數是某個片段來達到靈活佈局的目的:
<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 th:replace="base :: common_header(~{::title},~{})">
<title>Awesome - Main</title>
</head>
如果是想保留片段中的默認值,可以指定它對相應的參數不操作,如:
<head th:replace="base :: common_header(_,~{::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>
那麼片段中的title標記的默認值就會被保留。
21.5有條件插入片段
根據不同角色選用不同的片段:
<!--匹配上,則用片段,否則插入空片段-->
<div th:insert="${user.isAdmin()} ? ~{common :: adminhead} : ~{}">...</div>
<!--匹配上,則用片段,否則不改變模板標記-->
<div th:insert="${user.isAdmin()} ? ~{common :: adminhead} : ~{}">...</div>
21.6移除模板片段
使用th:remove
屬性移除片段,這個屬性有5種行爲方式:
- all:移除包含標籤及其孩子標籤
- body:不移除包含標籤,只移除其孩子標籤
- tag:只移除包含標籤,不移除其孩子標籤
- all-but-first:移除包含標籤的所有孩子標籤,除了第一個之外
- none:什麼也不做
移除也可以帶條件的:
<a href="/something" th:remove="${condition}? tag : none">Link text not to be removed</a>
由上可知th:remove
屬性會視null爲none:
<a href="/something" th:remove="${condition}? tag">Link text not to be removed</a>
21.7片段的繼承
定義一個片段layout,有兩個參數title和content:
<!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>
html標籤會被替換,title和content也會被各自替換。
22.本地變量
一般可以在th:each
迭代時定義本地變量,其實可以用th:with
定義本地變量:
<div th:with="firstPer=${persons[0]}">
<p>
The name of the first person is <span th:text="${firstPer.name}">Julius Caesar</span>.
</p>
</div>
也可以同時定義多個本地變量:
<div th:with="firstPer=${persons[0]},secondPer=${persons[1]}">
<p>
The name of the first person is <span th:text="${firstPer.name}">Julius Caesar</span>.
</p>
<p>
But the name of the second person is
<span th:text="${secondPer.name}">Marcus Antonius</span>.
</p>
</div>
th:with屬性允許重複利用本地變量:
<div th:with="company=${user.company + ' Co.'},account=${accounts[company]}">...</div>
23.th:block塊屬性
Thymeleaf中唯一一個作爲元素處理器的屬性th:block。th:block僅僅是一個屬性容器,如:
<table>
<th:block th:each="user : ${users}">
<tr>
<td th:text="${user.login}">...</td>
<td th:text="${user.name}">...</td>
</tr>
<tr>
<td colspan="2" th:text="${user.address}">...</td>
</tr>
</th:block>
</table>
本地變量user可以在th:block包圍的範圍內使用。
24.內聯表達式
如果有一個標籤是這樣寫的:
<p>Hello, <span th:text="${session.user.name}">Sebastian</span>!</p>
它完成span標籤來輔助完成,其實內聯表達式就可以完美達到這一點:
<p>Hello, [[${session.user.name}]]!</p>
等價於
<p>Hello, [(${session.user.name})]!</p>
[[...]]
或[(...)]
這兩種內聯表達式可以讓我們直接將表達式寫到html中去。
[[...]]
對應th:text
,[(...)]
對應th:utext
25.禁用內聯表達式
<p th:inline="none">A double array looks like this: [[1, 2, 3], [4, 5]]!</p>
26.文本內聯
需要顯示啓用。與上面看到的內聯表達式差不多,但更強大。
<div th:inline="text">
...
</div>
27.javascript內聯
需要顯示啓用。可以更好的對標籤<script>進行集成。
<script th:inline="javascript">
...
var username = [[${session.user.name}]];
...
</script>
28.CSS內聯
需要顯示啓用。可以更好的對標籤<style>進行集成。
<style th:inline="css">
.[[${classname}]] {
text-align: [[${align}]];
}
</style>
上面就是Thymeleaf3.0的大概教程了。學習完,也基本掌握Thymeleaf3.0了。