Appearance
Thymeleaf
模板引擎有哪些?
- JSP
- Velocity
- Thymeleaf
- FreeMarker
1.Thymeleaf简介
https://www.thymeleaf.org/doc/tutorials/3.1/usingthymeleaf.html
http://commons.apache.org/proper/commons-ognl/
https://www.kaifaxueyuan.com/frontend/thymeleaf3/thymeleaf3-setting-attribute-values.html
Thymeleaf是用来开发 Web 和独立环境项目的现代服务器端 Java 模板引擎。
Thymeleaf 的主要目标是为您的开发工作流程带来优雅的自然模板 - HTML。可以在直接浏览器中正确显示,并且可以作为静态原型,从而在开发团队中实现更强大的协作。
借助 Spring Framework 的模块,可以根据自己的喜好进行自由选择,可插拔功能组件,Thymeleaf 是现代 HTML5 JVM Web 开发的理想选择 - 尽管它可以做的更多。
Spring 官方支持的服务的渲染模板中,并不包含 jsp。而是 Thymeleaf 和 Freemarker 等,而 Thymeleaf 与 SpringMVC 的视图技术,及 SpringBoot 的自动化配置集成非常完美,几乎没有任何成本,你只用关注 Thymeleaf 的语法即可。
2.特点
- 动静结合:Thymeleaf 在有网络和无网络的环境下皆可运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。这是由于它支持 html 原型,然后在 html 标签里增加额外的属性来达到模板+数据的展示方式。浏览器解释 html 时会忽略未定义的标签属性,所以 thymeleaf 的模板可以静态地运行;当有数据返回到页面时,Thymeleaf 标签会动态地替换掉静态内容,使页面动态显示。
- 开箱即用:它提供标准和 spring 标准两种方言,可以直接套用模板实现 JSTL、 OGNL 表达式效果,避免每天套模板、改 jstl、改标签的困扰。同时开发人员也可以扩展和创建自定义的方言。
- 多方言支持:Thymeleaf 提供 spring 标准方言和一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能。
- 与 SpringBoot 完美整合,SpringBoot 提供了 Thymeleaf 的默认配置,并且为 Thymeleaf 设置了视图解析器,我们可以像以前操作 jsp 一样来操作 Thymeleaf。代码几乎没有任何区别,就是在模板语法上有区别。
快速入门
导入依赖
- pom.xml
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>practice-thymeleaf</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<!-- javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.7</version>
</dependency>
<!-- spring-aspect -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.7</version>
</dependency>
<!-- spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.7</version>
</dependency>
<!-- spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.7</version>
</dependency>
<!-- thymeleaf-spring5 -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.12.RELEASE</version>
</dependency>
</dependencies>
</project>
配置SpringMVC
- application-context.xml
xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/util
https://www.springframework.org/schema/util/spring-util.xsd">
<context:component-scan base-package="com.futureweaver"/>
<bean id="templateResolver"
class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver"
p:prefix=""
p:suffix=""
p:characterEncoding="UTF-8"
p:order="1"
p:templateMode="HTML5"
p:cacheable="false"/>
<bean id="templateEngine"
class="org.thymeleaf.spring5.SpringTemplateEngine"
p:templateResolver-ref="templateResolver"/>
<bean id="viewResolver"
class="org.thymeleaf.spring5.view.ThymeleafViewResolver"
p:templateEngine-ref="templateEngine"
p:characterEncoding="UTF-8"/>
</beans>
编写Controller
- WelcomeController.java
java
@Controller
public class WelcomeController {
@RequestMapping("/welcome")
public String index(Model model) {
model.addAttribute("name", "张三");
return "/index.html";
}
}
编写html
- index.html
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="${name}">h1 title</h1>
</body>
</html>
标准表达式语法
简单表达式
- 变量表达式:
${...}
- 选择表达式:
*{...}
- 消息表达式:
#{...}
- 链接表达式:
@{...}
- 片段表达式:
~{...}
- 变量表达式:
常量
- 文本常量:
'one text'
,'Another one!'
, ... - 数字常量:
0
,34
,3.0
,12.3
, ... - 布尔常量:
true
,false
- Null 常量:
null
- 常量标记:
one
,sometext
,main
, ...
- 文本常量:
文本操作
- 字符串级联:
+
- 常量替换:
|The name is ${name}|
- 字符串级联:
算术运算符
+
,-
,*
,/
,%
逻辑运算符
and
,or
,!
,not
比较运算符
>
,<
,>=
,<=
,==
,!=
gt
,lt
,ge
,le
,eq
,ne
条件运算符
- If-then:
(if) ? (then)
- If-then-else:
(if) ? (then) : (else)
- Default:
(value) ?: (defaultvalue)
- If-then:
特殊标记No-Operation: _
#{...}
消息表达式
home_en.properties
英语
home_es.properties
西班牙语
home_pt_BR.properties
巴西语
home.properties
默认语言
propertieshome.welcome=¡Bienvenido a nuestra tienda de comestibles, {0}!
home.html
html<p th:utext="#{home.welcome(${session.user.name})}"> Welcome to our grocery store, Sebastian Pepper! </p>
${...}
变量表达式
${...}
表达式实际上是在上下文中包含的变量映射上执行的OGNL表达式。
基本对象
#ctx
the context object.
#vars
the context variables.
#locale
the context locale.
#request
(only in Web Contexts) the HttpServletRequest object.
#response
(only in Web Contexts) the HttpServletResponse object.
#session
(only in Web Contexts) the HttpSession object.
#servletContext
(only in Web Contexts) the ServletContext object.
实用工具对象
#execInfo
information about the template being processed.
#messages
methods for obtaining externalized messages inside variables expressions, in the same way as they would be obtained using #{…} syntax.
#uris
methods for escaping parts of URLs/URIs
#conversions
methods for executing the configured conversion service (if any).
#dates
methods for java.util.Date objects: formatting, component extraction, etc.
#calendars
analogous to #dates, but for java.util.Calendar objects.
#numbers
methods for formatting numeric objects.
#strings
methods for String objects: contains, startsWith, prepending/appending, etc.
#objects
methods for objects in general.
#bools
methods for boolean evaluation.
#arrays
methods for arrays.
#lists
methods for lists.
#sets
methods for sets.
#maps
methods for maps.
#aggregates
methods for creating aggregates on arrays or collections.
#ids
methods for dealing with id attributes that might be repeated (for example, as a result of an iteration).
*{...}
选择表达式
html
<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>
@{...}
链接表达式
绝对路径
@{http://www.thymeleaf.org}
相对路径
相对页面
@{user/login.html}
相对上下文
@{/itemdetails?id=3}
相对服务器
@{~/billing/processInvoice}
html
<!-- 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>
~{...}
片段表达式
html
<div th:insert="~{commons :: main}">...</div>
html
<div th:with="frag=~{footer :: #main/text()}">
<p th:insert="${frag}">
</div>
Literals
Text literals
html
<p>
Now you are looking at a <span th:text="'working web application'">template file</span>.
</p>
Number literals
html
<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>
Boolean literals
html
<div th:if="${user.isAdmin()} == false"> ...
<div th:if="${user.isAdmin() == false}"> ...
The null literal
html
<div th:if="${variable.something} == null"> ...
Literal tokens
html
<div th:class="content">...</div>
<div th:class="'content'">...</div>
Appending texts
html
<span th:text="'The name of the user is ' + ${user.name}">
Literal substitutions
html
<span th:text="|Welcome to our application, ${user.name}!|">
<span th:text="'Welcome to our application, ' + ${user.name} + '!'">
<span th:text="${onevar} + ' ' + |${twovar}, ${threevar}|">
Arithmetic operations
html
<div th:with="isEven=(${prodStat.count} % 2 == 0)">
<div th:with="isEven=${prodStat.count % 2 == 0}">
Comparators and Equality
html
<div th:if="${prodStat.count} > 1">
<span th:text="'Execution mode is ' + ( (${execMode} == 'dev')? 'Development' : 'Production')">
Conditional expressions
html
<tr th:class="${row.even}? 'even' : 'odd'">
...
</tr>
<tr th:class="${row.even}? (${row.first}? 'first' : 'even') : 'odd'">
...
</tr>
<tr th:class="${row.even}? 'alt'">
...
</tr>
Default expressions (Elvis operator)
html
<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>
The No-Operation token
html
<span th:text="${user.name} ?: 'no user authenticated'">...</span>
<span th:text="${user.name} ?: _">no user authenticated</span>
Data Conversion / Formatting
html
<td th:text="${{user.lastAccessDate}}">...</td>
4.15 Preprocessing
Messages_fr.properties
properties
article.text=@myapp.translator.Translator@translateToFrench({0})
Messages_es.properties
properties
article.text=@myapp.translator.Translator@translateToSpanish({0})
- Messages.html
html
<p th:text="${__#{article.text('textVar')}__}">Some text here...</p>
<p th:text="${@myapp.translator.Translator@translateToFrench(textVar)}">Some text here...</p>
设置标签属性
设置任意标签属性
th:attr
html
<form action="subscribe.html" th:attr="action=@{/subscribe}">
<fieldset>
<input type="text" name="email" />
<input type="submit" value="Subscribe!" th:attr="value=#{subscribe.submit}"/>
</fieldset>
</form>
html
<input type="submit" value="Subscribe!" th:attr="value=#{subscribe.submit}"/>
th:attr
html
<img src="../../images/gtvglogo.png"
th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />
th:value
html
<input type="submit" value="Subscribe!" th:value="#{subscribe.submit}"/>
th:action
html
<form action="subscribe.html" th:action="@{/subscribe}">
th:href
html
<li><a href="product/list.html" th:href="@{/product/list}">Product List</a></li>
- 类似的语法还有很多
th:abbr | th:accept | th:accept-charset |
th:accesskey | th:action | th:align |
th:alt | th:archive | th:audio |
th:autocomplete | th:axis | th:background |
th:bgcolor | th:border | th:cellpadding |
th:cellspacing | th:challenge | th:charset |
th:cite | th:class | th:classid |
th:codebase | th:codetype | th:cols |
th:colspan | th:compact | th:content |
th:contenteditable | th:contextmenu | th:data |
th:datetime | th:dir | th:draggable |
th:dropzone | th:enctype | th:for |
th:form | th:formaction | th:formenctype |
th:formmethod | th:formtarget | th:fragment |
th:frame | th:frameborder | th:headers |
th:height | th:high | th:href |
th:hreflang | th:hspace | th:http-equiv |
th:icon | th:id | th:inline |
th:keytype | th:kind | th:label |
th:lang | th:list | th:longdesc |
th:low | th:manifest | th:marginheight |
th:marginwidth | th:max | th:maxlength |
th:media | th:method | th:min |
th:name | th:onabort | th:onafterprint |
th:onbeforeprint | th:onbeforeunload | th:onblur |
th:oncanplay | th:oncanplaythrough | th:onchange |
th:onclick | th:oncontextmenu | th:ondblclick |
th:ondrag | th:ondragend | th:ondragenter |
th:ondragleave | th:ondragover | th:ondragstart |
th:ondrop | th:ondurationchange | th:onemptied |
th:onended | th:onerror | th:onfocus |
th:onformchange | th:onforminput | th:onhashchange |
th:oninput | th:oninvalid | th:onkeydown |
th:onkeypress | th:onkeyup | th:onload |
th:onloadeddata | th:onloadedmetadata | th:onloadstart |
th:onmessage | th:onmousedown | th:onmousemove |
th:onmouseout | th:onmouseover | th:onmouseup |
th:onmousewheel | th:onoffline | th:ononline |
th:onpause | th:onplay | th:onplaying |
th:onpopstate | th:onprogress | th:onratechange |
th:onreadystatechange | th:onredo | th:onreset |
th:onresize | th:onscroll | th:onseeked |
th:onseeking | th:onselect | th:onshow |
th:onstalled | th:onstorage | th:onsubmit |
th:onsuspend | th:ontimeupdate | th:onundo |
th:onunload | th:onvolumechange | th:onwaiting |
th:optimum | th:pattern | th:placeholder |
th:poster | th:preload | th:radiogroup |
th:rel | th:rev | th:rows |
th:rowspan | th:rules | th:sandbox |
th:scheme | th:scope | th:scrolling |
th:size | th:sizes | th:span |
th:spellcheck | th:src | th:srclang |
th:standby | th:start | th:step |
th:style | th:summary | th:tabindex |
th:target | th:title | th:type |
th:usemap | th:value | th:valuetype |
th:vspace | th:width | th:wrap |
th:xmlbase | th:xmllang | th:xmlspace |
Appending and prepending
- th:attrappend
- th:attrprepend
html
<input type="button" value="Do it!" class="btn" th:attrappend="class=${' ' + cssStyle}" />
<!-- If you process this template with the cssStyle variable set to "warning", you will get: -->
<!-- <input type="button" value="Do it!" class="btn warning" /> -->
- th:classappend
- th:styleappend
html
<tr th:each="prod : ${prods}" class="row" th:classappend="${prodStat.odd}? 'odd'">
Fixed-value boolean attributes
- th:checked
html
<input type="checkbox" name="active" th:checked="${user.active}" />
- 类似的语法还有很多
th:async | th:autofocus | th:autoplay |
th:checked | th:controls | th:declare |
th:default | th:defer | th:disabled |
th:formnovalidate | th:hidden | th:ismap |
th:loop | th:multiple | th:novalidate |
th:nowrap | th:open | th:pubdate |
th:readonly | th:required | th:reversed |
th:scoped | th:seamless | th:selected |
循环
html
<ul th:if="${condition}">
<li th:each="u : ${users}" th:text="${u.name}">user name</li>
</ul>
<!-- 1, 21, 41, 61, 81, 101, 121, 141, 161, 181 -->
<option th:each="x : ${#numbers.sequence(1, 200, 20)}">
<p th:text="${ x }"></p>
</option>
<!-- 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 -->
<option th:each="x : ${#numbers.sequence(1, 200/20)}">
<p th:text="${ x }"></p>
</option>
条件判断
th:if
html
<a href="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
th:if="${not #lists.isEmpty(prod.comments)}">view</a>
请注意,th:if属性不仅计算布尔条件。它的能力远远不止于此,它将按照以下规则将指定的表达式评估为真:
- If value is not null:
- If value is a boolean and is true.
- If value is a number and is non-zero
- If value is a character and is non-zero
- If value is a String and is not “false”, “off” or “no”
- If value is not a boolean, a number, a character or a String.
- (If value is null, th:if will evaluate to false).
th:unless
th:if
的逆属性
html
<a href="comments.html"
th:href="@{/comments(prodId=${prod.id})}"
th:unless="${#lists.isEmpty(prod.comments)}">view</a>
switch
html
<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>
SpringBoot整合Thymeleaf
导入依赖
- pom.xml
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
配置
注意,默认情况下,Thymeleaf的模板文件,会到
resources/templates
找
yml
spring:
thymeleaf:
cache: false
encoding: UTF-8
#prefix: classpath:/static/
#suffix: .html
mode: HTML