前端渲染模板(一):Thymeleaf
一、使用
本篇文章将以SpringBoot为框架来介绍Thymeleaf的用法。
1 资源文件的约定目录结构
Maven的资源文件目录:/src/java/resources
spring-boot项目静态文件目录:/src/java/resources/static
spring-boot项目模板文件目录:/src/java/resources/templates
spring-boot静态首页的支持,即index.html放在以下目录结构会直接映射到应用的根目录下:
classpath:/META-INF/resources/index.html
classpath:/resources/index.html
classpath:/static/index.html
calsspath:/public/index.html
因此,SpringBoot默认的静态资源文件应该放在resources/tatic下,而静态页面应该放在resources/templates文件夹下,这两个文件不会自动创建,需要手动创建,如下图所示:
2 Maven依赖:
1 <!-- thymeleaf 前端渲染模板依赖 --> 2 <dependency> 3 <groupId>org.springframework.boot</groupId> 4 <artifactId>spring-boot-starter-thymeleaf</artifactId> 5 <version>2.0.0.RELEASE</version> 6 </dependency>
3 html页面中(<html>标签处):
1 <html xmlns="http://www.w3.org/1999/xhtml" 2 xmlns:th="http://www.thymeleaf.org" 3 xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
4 application.properties配置(application.yml则根据格式相应变化即可):
1 # THYMELEAF (ThymeleafAutoConfiguration) 2 #开启模板缓存(默认值:true) 3 spring.thymeleaf.cache=true 4 #Check that the template exists before rendering it. 5 spring.thymeleaf.check-template=true 6 #检查模板位置是否正确(默认值:true) 7 spring.thymeleaf.check-template-location=true 8 #Content-Type的值(默认值:text/html) 9 spring.thymeleaf.content-type=text/html 10 #开启MVC Thymeleaf视图解析(默认值:true) 11 spring.thymeleaf.enabled=true 12 #模板编码 13 spring.thymeleaf.encoding=UTF-8 14 #要被排除在解析之外的视图名称列表,用逗号分隔 15 spring.thymeleaf.excluded-view-names= 16 #要运用于模板之上的模板模式。另见StandardTemplate-ModeHandlers(默认值:HTML5) 17 spring.thymeleaf.mode=HTML5 18 #在构建URL时添加到视图名称前的前缀(默认值:classpath:/templates/) 19 spring.thymeleaf.prefix=classpath:/templates/ 20 #在构建URL时添加到视图名称后的后缀(默认值:.html) 21 spring.thymeleaf.suffix=.html 22 #Thymeleaf模板解析器在解析器链中的顺序。默认情况下,它排第一位。顺序从1开始,只有在定义了额外的TemplateResolver Bean时才需要设置这个属性。 23 spring.thymeleaf.template-resolver-order= 24 #可解析的视图名称列表,用逗号分隔 25 spring.thymeleaf.view-names=
配置好以上的代码,此时在html模板文件中动态的属性使用th:命名空间修饰,就可以使用Thymeleaf了。
二、语法
1 引用静态资源文件:
比如CSS和JS文件,语法格式为“@{}”,如@{/js/blog/blog.js}会引入/static目录下的/js/blog/blog.js文件。
2 访问spring-mvc中model的属性:
语法格式为“${}”,如${user.id}可以获取model里的user对象的id属性,类似JSTL。
3 循环:
在html的标签中,加入th:each=“value:${list}”形式的属性,如<span th:each=”user:${users}”></span>可以迭代users的数据
4 判断:
在html标签中,加入th:if=”表达式”可以根据条件显示html元素
1 <span th:if="${not #lists.isEmpty(blog.publishTime)}"> 2 <span id="publishtime" th:text="${#dates.format(blog.publishTime, 'yyyy-MM-dd HH:mm:ss')}"></span> 3 </span>
以上代码表示若blog.publishTime时间不为空,则显示时间。
5 时间格式化:
1 ${#dates.format(blog.publishTime,'yyyy-MM-dd HH:mm:ss')} //表示将时间格式化为”yyyy-MM-dd HH:mm:ss”格式化写法与Java格式化Date的写法是一致的。
6 字符串拼接:
有两种形式 ,比如拼接这样一个URL:/blog/delete/{blogId}
第一种:th:href="'/blog/delete/' + ${blog.id }"
第二种:th:href="${'/blog/delete/' + blog.id }"
7 为CSS样式的某些路径使用thyemleaf表达式:
1 <div th:style="'background:url(' + @{/<path-to-image>} + ');'"></div>
8 使用th:utext="@{}" 可以让变量里面的html标签被解析
三、表达式
1 简单表达式 (simple expressions)
1 ${...} 变量表达式 2 3 *{...} 选择变量表达式 4 5 #{...} 消息表达式 6 7 @{...} 链接url表达式
2 字面量
1 'one text','another one!',... 文本 2 3 0,34,3.0,12.3,... 数值 4 5 true false 布尔类型 6 7 null 空 8 9 one,sometext,main 文本字符
3 文本操作
1 + 字符串连接 2 3 |The name is ${name}| 字符串连接
4 算术运算
1 + , - , * , / , % 二元运算符 2 3 - 负号(一元运算符)
5 布尔操作
1 and,or 二元操作符 2 3 !,not 非(一元操作符)
6 关系操作符
1 > , < , >= , <= (gt , lt , ge , le) 2 3 == , != (eq, ne)
7 条件判断
1 (if) ? (then) if-then 2 3 (if) ? (then) : (else) if-then-else 4 5 <tr th:class="${row.even}? 'even' : 'odd'"> 6 ... 7 </tr>
条件表达式中的三个部分自身也可以是表达式,也可以是变量(${...}, *{...}), 消息(#{...}), URL (@{...}) 或字面量 ('...')
条件表达式也可以使用括号来嵌套:
1 <tr th:class="${row.even}? (${row.first}? 'first' : 'even') : 'odd'"> 2 ... 3 </tr>
else表达式也可以省略,当条件为false时,会返回null:
1 <tr th:class="${row.even}? 'alt'"> 2 ... 3 </tr> 4 (value) ?: (defaultvalue) Default
只有在第一个表达式返回null时,第二个表达式才会运算
8 表达式工具对象
1 #dates 与java.util.Date对象的方法对应,格式化、日期组件抽取等等 2 #calendars 类似#dates,与java.util.Calendar对象对应 3 #numbers 格式化数字对象的工具方法 4 #strings 与java.lang.String对应的工具方法:contains、startsWith、prepending/appending等等 5 #objects 用于对象的工具方法 6 #bools 用于布尔运算的工具方法 7 #arrays 用于数组的工具方法 8 #lists 用于列表的工具方法 9 #sets 用于set的工具方法 10 #maps 用于map的工具方法 11 #aggregates 用于创建数组或集合的聚合的工具方法 12 #messages 用于在变量表达式内部获取外化消息的工具方法,与#{…}语法获取的方式相同 13 #ids 用于处理可能重复出现(例如,作为遍历的结果)的id属性的工具方法
9 链接URL
URL在web模板中是一级重要元素,使用@{…}表示
URL的类型:
绝对URL:
http://www.thymeleaf.org
相对URL:
1 页面相对: user/login.html 2 上下文相对:/itemdetails?id=3 (服务器上下文名称会被自动添加) 3 服务器相对:~/billing/processInvoice(允许调用同一服务器上的另一个上下文中的URL) 4 协议相对://code.jquery.com/jquery-2.0.3.min.js
Thymeleaf在任何情况下都可以处理绝对URL,对于相对URL,则需要使用一个实现了IWebContext接口的上下文对象,这个对象包含了来自HTTP请求的信息,这些信息用于创建相对链接。
1 <!-- Will produce 'http://localhost:8080/gtvg/order/details?orderId=3' (plus rewriting) --> 2 <a href="details.html" th:href="@{http://localhost:8080/gtvg/order/details(orderId=${o.id})}">view</a> 3 4 <!-- Will produce '/gtvg/order/details?orderId=3' (plus rewriting) --> 5 <a href="details.html" th:href="@{/order/details(orderId=${o.id})}">view</a> 6 7 <!-- Will produce '/gtvg/order/3/details' (plus rewriting) --> 8 <a href="details.html" th:href="@{/order/{orderId}/details(orderId=${o.id})}">view</a>
10 预处理
Thymeleaf提供预处理表达式的功能。
它是在表壳式正常执行前执行的操作,允许修改最终将要被执行的表达式。
预处理表达式跟正常的一样,但被两个下划线包围住,例如:__${expression}__
假设有一个i18n消息文件Message_fr.properties,里面有一个条目包含了一个调用具体语言的静态方法的OGNL表达式:
article.text=@myapp.translator.Translator@translateToFrench({0})
Messages_es.properties中的等价条目:
article.text=@myapp.translator.Translator@translateToSpanish({0})
可以根据locale先创建用于运算表达式的标记片段,本例中,先通过预处理选择表达式,然后让Thymeleaf处理这个选择出来的表达式:
<p th:text="${__#{article.text('textVar')}__}">Some text here...</p>
对于locale为French的情况,上面的表达式经过预处理后,得出的等价物如下:
<p th:text="${@myapp.translator.Translator@translateToFrench(textVar)}">Some text here...</p>
四、 设置属性值
1 th:attr 任何属性值
1 <form action="subscribe.html" th:attr="action=@{/subscribe}"> 2 <fieldset> 3 <input type="text" name="email" /> 4 <input type="submit" value="Subscribe me!" th:attr="value=#{subscribe.submit}"/> 5 </fieldset> 6 </form>
多个属性一起设置,用逗号隔开
<img src="../../images/gtvglogo.png" th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />
2 设置指定属性
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 ...
1 <input type="submit" value="Subscribe me!" th:value="#{subscribe.submit}"/> 2 <form action="subscribe.html" th:action="@{/subscribe}"> 3 <li><a href="product/list.html" th:href="@{/product/list}">Product List</a></li>
设置多个属性在同一时间 有两个特殊的属性可以这样设置: th:alt-title 和 th:lang-xmllang
th:alt-title 设置 alt 和 title
th:lang-xmllang 设置 lang 和 xml:lang
1 <img src="../../images/gtvglogo.png" th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" /> 2 3 <img src="../../images/gtvglogo.png"th:src="@{/images/gtvglogo.png}" th:title="#{logo}" th:alt="#{logo}" /> 4 5 <img src="../../images/gtvglogo.png"th:src="@{/images/gtvglogo.png}" th:alt-title="#{logo}" />
前置和后置添加属性值 th:attrappend 和 th:attrprepend
<input type="button" value="Do it!" class="btn" th:attrappend="class=${' ' + cssStyle}" />
编译后:
<input type="button" value="Do it!" class="btn warning" />
还有两个特定的添加属性 th:classappend 和 th:styleappend
<tr th:each="prod : ${prods}" class="row" th:classappend="${prodStat.odd}? 'odd'">
3 修复的布尔属性
<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
HTML5友好的属性及元素名
1 <table> 2 <tr data-th-each="user : ${users}"> 3 <td data-th-text="${user.login}">...</td> 4 <td data-th-text="${user.name}">...</td> 5 </tr> 6 </table>
注:data-{prefix}-{name}是编写HTML5自定义属性的标准语法,不需要开发者使用th:*这样的命名空间,Thymeleaf让这种语法自动对所有dialect都可用。
五、遍历
1 基础
1 <tr th:each="prod : ${prods}"> 2 <td th:text="${prod.name}">Onions</td> 3 <td th:text="${prod.price}">2.41</td> 4 <td th:text="${prod.inStock}? #{true} : #{false}">yes</td> 5 </tr>
可遍历的对象:实现java.util.Iterable、java.util.Map(遍历时取java.util.Map.Entry)、array、任何对象都被当作只有对象自身一个元素的列表
2 状态
当前遍历索引,从0开始,index属性
当前遍历索引,从1开始,count属性
总元素数量,size属性
每一次遍历的iter变量,current属性
当前遍历是even还是odd,even/odd布尔属性
当前遍历是第一个,first布尔属性
当前遍历是最后一个,last布尔属性
1 <tr th:each="prod,iterStat : ${prods}" th:class="${iterStat.odd}? 'odd'"> 2 <td th:text="${prod.name}">Onions</td> 3 <td th:text="${prod.price}">2.41</td> 4 <td th:text="${prod.inStock}? #{true} : #{false}">yes</td> 5 </tr>
若不指定状态变量,Thymeleaf会默认生成一个名为“变量名Stat”的状态变量:
1 <tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'"> 2 <td th:text="${prod.name}">Onions</td> 3 <td th:text="${prod.price}">2.41</td> 4 <td th:text="${prod.inStock}? #{true} : #{false}">yes</td> 5 </tr>
六、条件运算
1 <tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'"> 2 <td th:text="${prod.name}">Onions</td> 3 <td th:text="${prod.price}">2.41</td> 4 <td th:text="${prod.inStock}? #{true} : #{false}">yes</td> 5 <td> 6 <span th:text="${#lists.size(prod.comments)}">2</span> comment/s 7 <a href="comments.html" th:href="@{/product/comments(prodId=${prod.id})}" th:if="${not #lists.isEmpty(prod.comments)}">view</a> 8 </td> 9 </tr>
<a href="comments.html" th:href="@{/product/comments(prodId=${prod.id})}" th:if="${not #lists.isEmpty(prod.comments)}">view</a>
th:if 不只运算布尔条件,它对以下情况也运算为true:
值不为null
值为boolean且为true
值为数字且非0
值为字符且非0
值是字符串且不是:“false”,“off”,“no”
值不是boolean、数字、字符、字符串
如果值为null,则th:if运算结果为false
th:if的反面是th:unless
1 <a href="comments.html" th:href="@{/comments(prodId=${prod.id})}" th:unless="${#lists.isEmpty(prod.comments)}">view</a> 2 th:switch 和 th:case 3 4 <div th:switch="${user.role}"> 5 <p th:case="'admin'">User is an administrator</p> 6 <p th:case="#{roles.manager}">User is a manager</p> 7 </div> 8 9 <div th:switch="${user.role}"> 10 <p th:case="'admin'">User is an administrator</p> 11 <p th:case="#{roles.manager}">User is a manager</p> 12 <p th:case="*">User is some other thing</p> 13 </div>
七、坑
总结一下在使用过程中遇到的坑:
1 获取项目根目录,很多时候我们都需要获取本项目的根目录,但是使用的代码是以下代码才能获得:
<script th:inline="javascript" type="text/javascript"> var ctx=[[${#httpServletRequest.getContextPath()}]]; </script>
注意以上代码,如果需要在自定义的javascript中使用thymeleaf表达式,则需要将script声明一下:th:inline="javascript"。
2 获取图片路径,当我们使用thymeleaf的时候一定要注意单、双引号的区别,不然是无法获取到对应的值或者路径的:
<span class="img" th:style="'background-image: url('+@{/images/news/}+${top4News[2].originArticleId}+'/1.jpg'+');'"></span>
请注意以上代码:当我们要在backgroud-image这个标签下引用对应路径的img时:
首先使用th:style进行修饰。
然后在background-image标签前后一定要加单引号,否则不会起作用:
'background-image:xxx'
接着就是在url里面拼接你的img路径,请注意url('xxx'),也有单引号!
最后才能通过此路径获取到相应的图片。
八、总结
在使用thymeleaf的过程中,遇到了一些坑,希望大家在这样的坑中少走一些弯路。