课程表

Spring Boot课程

工具箱
速查手册

Boot Thymeleaf高级教程

当前位置:免费教程 » Java相关 » Spring Boot
注意:本页面内容为W3xue原创,未经授权禁止转载,违者必究!
来源:W3xue  发布时间:2019/9/29 12:31:24

上一节中我们已经简单介绍了Spring Boot 中 Thymeleaf的基本配置和使用,本节让我们来看看其更加详尽的用法。本节课程主要分为以下几部分:

一、标准表达式语法

二、表达式支持的语法

三、常用th标签

四、Thymeleaf 的布局


一、标准表达式语法

标准表达式分一共为四类:

1.变量表达式($)

2.选择或星号表达式(*)

3.文字国际化表达式(#)

4.URL 表达式(@)

下面我们分别来介绍。

1.变量表达式

变量表达式即 OGNL 表达式或 Spring EL 表达式(在 Spring 术语中也叫 model attributes)。如下所示:

  1. ${session.user.name}

其中点号,表明它是对象的成员,是上下级关系。我们来看一个例子。在“lucky.html”底部,增加2个标签:

  1. <p th:text="${student1.name}"></p>
  2. <li th:each="stu : ${students}"><span th:text="${stu.name}"></span></li>

当然,这里的student1和students都是预设的标签名字,我们还没有在后端绑定数据给它。此外,这里出现了一个新语法“th:each”,我们后面也会讲到。我们现在就来给这2个标签绑定数据,首先需要先在MainController类的顶部,添加自动装配的代码:

  1. @Autowired
  2. private MainDao mainDao;

然后我们在MainController类中的getlucky路由(也是方法名)中,添加如下的绑定:

  1. MainBean mbstudent1=mainDao.getOne(1); //取得ID为1的学生对象
  2. mv.addObject("student1",mbstudent1); //将其绑定到student1标签上
  3. List<MainBean> mbStudents=mainDao.getStudentByName(""); //没有关键词,将会选择全部学生
  4. mv.addObject("students",mbStudents);//将其绑定到students标签上

你会发现,访问“http://localhost:8080/jiaocheng/getlucky”时会多出如下HTML代码,这些都是取得的结果:

  1. <p>张三</p>    
  2. <li><span>张三</span></li>    
  3. <li><span>李四</span></li>    
  4. <li><span>王麻子</span></li>

2.选择或星号表达式(*)

星号表达式,提供了一种容器,这个容器里的标签,不必在重复写对象的名字,只需要写对象的成员变量名即可。我们来看一个例子,在“lucky.html”底部,增加1个标签即可,这里我们借用上一小节绑定的student1标签:

  1. <p th:object="${student1}">
  2.     姓名:<span th:text="*{name}"></span>,年龄:<span th:text="*{age}"></span>,年级:<span th:text="*{grade}"></span>
  3. </p>

这里,我们使用了“th:object”标签,表明th标签里将包含对象。

其实以上代码完全等价于:

  1. <p th:object="${student1}">
  2.     姓名:<span th:text="${student1.name}"></span>,年龄:<span th:text="${student1.age}"></span>,年级:<span th:text="${student1.grade}"></span>
  3. </p>

可以发现,星号表达式是省去了选择上文的代码,它可以与美元符号混合使用。

重新运行程序后,访问“http://localhost:8080/jiaocheng/getlucky”时会多出如下HTML代码:

  1. <p>
  2. 姓名:<span>张三</span>,年龄:<span>8</span>,年级:<span>二年级</span>    
  3. </p>

3.文字国际化表达式(#)

文字国际化表达式作用是,从另外一个.properties的外部文件获取某个标签的值。这个.properties文件,和application.yml(或默认的application.properties)一样,存放在“src/main/resources”文件夹。文字国际化表达式和引用application.yml的键值类似,这里不再详细介绍。

4.URL 表达式(@)

URL 表达式指的是把一个有用的上下文或会话信息添加到 URL,这个过程经常被叫做 URL 重写。比如:

  1. @{/getlucky}

还可以设置参数:

  1. @{/getlucky(id=${studentId})}

或者这样表示相对路径:

  1. @{../hello}

在“lucky.html”页面底部,添加下面的代码:

  1. <a href="https://www.w3xue.com/" th:href="@{/hello}">链接到哪里</a>

这个链接会最终指向“http://localhost:8080/jiaocheng/hello”。


二、表达式支持的语法

我们仍然有许多疑问,比如,如果想在th标签里直接表示文本,或者使用if判断,还有上文提到的循环each语法怎么办?我们一起来看看Thymeleaf标签支持的语法:

1、字面(Literals)

2、文本操作(Text operations)

3、算术运算(Arithmetic operations)

4、布尔操作(Boolean operations)

5、比较和等价(Comparisons and equality)

6、条件运算符(Conditional operators)

7、循环运算符

下面分别来一一介绍。

1、字面(Literals)

字面语法是最常用的语法,它包含以下具体类型:

·文本文字(Text literals): 'one text', 'Another one!',…

·数字文本(Number literals): 0, 34, 3.0, 12.3,…

·布尔文本(Boolean literals):true, false

·空(Null literal):null

·文字标记(Literal tokens):one, sometext, main,…

比如,我想要在th标签中,显示原始的文本,就需要用到单引号,然后让其与标签混合。我们可以在“lucky.html”页面底部,添加下面的代码:

  1. <p th:text="'学生的姓名是:'+${student1.name}"></p>

我们会发现,最终结果是:

  1. <p>学生的姓名是:张三</p>

数字文本常常会和运算符(如加减乘除)一起使用,布尔文本、null常常与if一起使用,后面会提到运算符和if语法。

2、文本操作(Text operations)

文本操作在上文中我们已经用过,即拼接原始文本和标签的+号。文本操作包括:

·字符串连接(String concatenation):+

·文本替换(Literal substitutions):|姓名是: ${name}|

单竖线允许原始文本和标签混用而不用使用单引号和+号连接,例如,上一个例子中使用单引号与下面的例子完全等价:

  1. <p th:text="|学生的姓名是:${student1.name}|"></p>

3、算术运算(Arithmetic operations)

算术运算主要用于数字的操作,包括下面2中类型:

·二元运算符(Binary operators):+, -, *, /, %

·减号(单目运算符)Minus sign (unary operator):-

例如,二元运算符可以将下面代码中该学生的年龄加10岁:

  1. <p th:text="${student1.age}+10"></p>

将显示为:

  1. <p>18</p>

但是,二元运算符中的+号却不能与文本操作符(+|)还有单引号配合使用,否则,将不进行计算,将被视为字符串连接的+号。

  1. <p th:text="'学生10年后的年龄是:'+${student1.age}+10"></p>

将显示为:

  1. <p>学生10年后的年龄是:810</p>

但是除法却可以混用,例如:

  1. <p th:text="'学生年龄的一半是:'+${student1.age}/2"></p>

将显示为:

  1. <p>学生年龄的一半是:4</p>

4、布尔操作(Boolean operations)

布尔操作语法主要包括2类:

·二元运算符(Binary operators):and, or

·布尔否定(一元运算符)Boolean negation (unary operator):!, not

在“lucky.html”页面底部,添加下面的代码:

  1. <p th:text="${student1.age}==8 and ${student1.name}=='李四'"></p>
  2. <p th:text="${student1.age}==8 or ${student1.name}=='李四'"></p>
  3. <p th:text="!${student1.age}==8">张三的年龄为8岁,但是加上否定前缀后,这里将显示false</p>
  4. <p th:text="not ${student1.age}==8">张三的年龄为8岁,但是加上否定前缀后,这里将显示false</p>

将显示如下的结果:

  1. <p>false</p>
  2. <p>true</p>
  3. <p>false</p>
  4. <p>false</p>

5、比较和等价(Comparisons and equality)

比较运算符在上一个例子中已经使用了一个(==,即一般程序语言中的是否相等判断),主要分为以下2类:

·比较(Comparators):>, <, >=, <= (gt, lt, ge, le)

·等值运算符(Equality operators):==, != (eq, ne)

这跟大多数程序语言的用法相同,这里不再赘述。

6、条件运算符(Conditional operators)

条件运算符对应一般程序语言中的if-else分支结构,可以分为以下几类:

·If/unless-then结构:(if/unless) ? (then)

·If-then-else结构(三目运算符):(if) ? (then) : (else)

·Default结构: (表达式或值 ?: defaultvalue)

·switch结构:switch-case

我们来看第一种结构,在“lucky.html”页面底部,添加下面的代码:

  1. <p th:if="${student1.age==8}" >学生的年龄是8</p>
  2. <p th:unless="${student1.age==8}" >学生的年龄不是8</p>

第一行是说,如果这个学生的年龄是8,就显示该段落。第二行正好相反,如果年龄是8则不显示该段落。

结果自然是这样,第二段没有显示出来:

  1. <p >学生的年龄是8</p>

第二种结构实际上是一种三目运算符,而第三种Default结构,它是一种判断表达式或值是否有效的结构,如果表达式或值有效,就使用该值,否则,就使用“defaultvalue”:

  1. <p th:text="( ${student1.age}==8 ? '是的' : '不是' )"></p>
  2. <p th:text="( ${student1.age}==9 ? '是的' : ( ${student1.age} ?: '未知' ) )"></p>

上面的代码运行结果为:

  1. <p>是的</p>
  2. <p>8</p>

这个例子表明,所有这些Thymeleaf支持的语法可以任意的进行组合。

第四种,switch结构,使用方法如下:

  1. <div th:switch="${student1.age}">
  2.     <p th:case="7">年龄为7岁</p>
  3.     <p th:case="8">年龄为8岁</p>
  4.     <p th:case="*">年龄默认值</p>
  5. </div>

其结果如下:

  1. <div>
  2. <p>年龄为8岁</p>
  3. </div>

7、循环运算符

最后我们来看看循环运算符,之前我们就看到了一个例子:

  1. <li th:each="stu : ${students}"><span th:text="${stu.name}"></span></li>

这个例子中,会把students标签包含的所有成员,都拿出来循环打印一遍。而students标签包含的单个成员,在这里被代指为“stu”。

实际上,我们还可以带入一个状态变量,例如,我们把上一个例子改成如下的形态:

  1. <li th:each="stu,i: ${students}"><span th:text="${i.count}+','"></span><span th:text="${stu.name}"></span></li>

这里,在“stu”后面多了一个i,这里被用来作为计数器了,后面用到了${i.count}。上面的代码结果如下:

  1. <li><span>1,</span><span>张三</span></li>
  2. <li><span>2,</span><span>李四</span></li>
  3. <li><span>3,</span><span>王麻子</span></li>

i这个状态变量,除了“count”计数器属性,还有其他的属性:

·index:当前迭代对象的 index(从0开始计算)

·count: 当前迭代对象的 index(从1开始计算)

·size:被迭代对象的大小

·current:当前迭代变量

·even/odd:布尔值,当前循环是否是偶数/奇数(从0开始计算)

·first:布尔值,当前循环是否是第一个

·last:布尔值,当前循环是否是最后一个


三、常用th标签

我们在上面已经使用了th:text,th:utext,th:href,th:inline,th:object,th:each,th:if,th:unless等标签,其实,Thymeleaf还有其他更多的标签,如下表所示:

关键字功能介绍案例
th:id替换id<input th:id="'xxx' + ${collect.id}"/>
th:text文本替换<p th:text="${collect.description}">description</p>
th:utext支持html的文本替换<p th:utext="${htmlcontent}">conten</p>
th:object替换对象<div th:object="${session.user}">
th:value属性赋值<input th:value="${user.name}" />
th:with变量赋值运算<div th:with="isEven=${prodStat.count}%2==0"></div>
th:style设置样式th:style="'display:' + @{(${sitrue} ? 'none' : 'inline-block')} + ''"
th:onclick点击事件th:onclick="'getCollect()'"
th:each属性赋值tr th:each="user,userStat:${users}">
th:if判断条件<a th:if="${userId == collect.userId}" >
th:unless和th:if判断相反<a th:href="@{/login}" th:unless=${session.user != null}>Login</a>
th:href链接地址<a th:href="@{/login}" th:unless=${session.user != null}>Login</a> />
th:switch多路选择 配合th:case 使用<div th:switch="${user.role}">
th:caseth:switch的一个分支<p th:case="'admin'">User is an administrator</p>
th:fragment布局标签,定义一个代码片段,方便其它地方引用<div th:fragment="alert">
th:include布局标签,替换内容到引入的文件<head th:include="layout :: htmlhead" th:with="title='xx'"></head> />
th:replace布局标签,替换整个标签到引入的文件<div th:replace="fragments/header :: title"></div>
th:insert布局标签,插入整个标签到引入的文件<div th:insert="fragments/header :: title"></div>
th:selectedselected选择框 选中th:selected="(${xxx.id} == ${configObj.dd})"
th:src图片类地址引入<img class="img-responsive" alt="App Logo" th:src="@{/img/logo.png}" />
th:inline定义js脚本可以使用变量<script type="text/javascript" th:inline="javascript">
th:action表单提交的地址<form action="subscribe.html" th:action="@{/subscribe}">
th:remove删除某个属性<tr th:remove="all"> 1.all:删除包含标签和所有的孩子。2.body:不包含标记删除,但删除其所有的孩子。3.tag:包含标记的删除,但不删除它的孩子。4.all-but-first:删除所有包含标签的孩子,除了第一个。5.none:什么也不做。这个值是有用的动态评估。
th:attr设置标签属性,多个属性可以用逗号分隔比如th:attr="src=@{/image/aa.jpg},title=#{logo}",此标签不太优雅,一般用的比较少。
th:attrappend追加标签属性比如<p class="btn" th:attrappend="class=${' ' + classname}" />,会在class上追加一个后台绑定的classname值
th:block跟HTML标签平行的标签,而不作为HTML标签的属性使用,在结果中会消失比如<th:block><p>这里是文本</p></th:block>,只会显示为<p>这里是文本</p>,一般用在布局上

还有非常多的标签,这里只列出最常用的几个,由于一个标签内可以包含多个th:x属性,其生效的优先级顺序为:

include,each,if/unless/switch/case,with,attr/attrprepend/attrappend,value/href,src ,etc,text/utext,fragment,remove。

例如,我们可以2个标签,让学生的名字链接到其详细信息页:

  1. <a th:text="${student1.name}+'的信息'" th:href="@{/stuinfo(id=${student1.id})}"></a>

结果为:

  1. <a href="/jiaocheng/stuinfo?id=1">张三的信息</a>

四、Thymeleaf 的布局

在引入布局之前,让我们先了解三个标签:th:insert、th:replace、th:include。我们先来看一个例子,在“lucky.html”页面底部,添加下面的代码:

  1. <p id="someEle">我是一个模板文本框</p>
  2. <p th:insert="this::#someEle"></p>
  3. <p th:replace="this::#someEle"></p>
  4. <p th:include="this::#someEle"></p>

你会得到如下结果:

  1. <p id="someEle">我是一个模板文本框</p>    
  2. <p><p id="someEle">我是一个模板文本框</p></p>    
  3. <p id="someEle">我是一个模板文本框</p>    
  4. <p>我是一个模板文本框</p>

第一个是原始的结果,底下三行可以看出三个标签的区别,th:insert标签是在原有控件内部插入被引用的控件th:replace是拿被引用的控件替换原有控件th:include是拿被引用的控件内容插入到原有控件中。Thymeleaf 3.0+版本不推荐使用th:include,所以我们应当避免使用(感谢网友“周”QQ67****330提醒原有错误,奖励5元现金红包)。网上其他教程说th:replace将被抛弃,这种说法属于以讹传讹,是错误的。

我们可以在其他的页面上引用这个控件,语法是“文件名::#控件ID”。首先,我们在“templates”文件夹,新建一个“greeting.html”文件,然后放入标签引用:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <title>Title</title>
  6. </head>
  7. <body>
  8. <p th:insert="lucky::#someEle"></p>
  9. <p th:replace="lucky::#someEle"></p>
  10. <p th:include="lucky::#someEle"></p>
  11. </body>
  12. </html>

然后,在MainController类,申明如下路由和方法:

  1. @RequestMapping(value = "/greeting")
  2. public ModelAndView greeting(ModelAndView mv) {
  3.     mv.setViewName("/greeting");
  4.     return mv;
  5. }

最后访问“http://localhost:8080/jiaocheng/greeting”,得到如下结果(body中的主要内容):

  1. <p><p id="someEle">我是一个模板文本框</p></p>    
  2. <p id="someEle">我是一个模板文本框</p>    
  3. <p>我是一个模板文本框</p>

现在我们来看看布局,我们在“lucky.html”页面底部,添加下面的代码:

  1. <th:block th:fragment="commonFooter">
  2. <footer>这里是页脚<br />然后是一堆备案、版权的文字</footer>
  3. </th:block>

注意,这里使用的是“th:block”标签,这个标签比较特殊,不同于其他标签作为HTML控件的属性,它和HTML控件是平行的,但是最终它会被删掉,但是它注册的布局片段却可以起作用。

然后在greeting.html”底部加上这样的代码:

  1. <th:block th:insert="lucky::commonFooter"></th:block>

这里跟刚才的插入控件的语法有一点不同,那就是没有“#”号,直接使用布局片段的名字就可以了。

以后,不管是什么模板文件,只要引用“<th:block th:insert="lucky::commonFooter"></th:block>”这样的代码,就会自动插入lucky.html中定义的这个代码段,一旦修改lucky.html中的这个代码段,其他页面显示的内容会自动更改,非常适合页眉、页脚这种高重复使用的代码段。

当然,建议把这种高度重复的代码,放到专门的模板文件里,方便其他页面集中引用。同时,引用还可以附带上参数。比如,我们在“templates”文件夹,再建一个“iframe.html”:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <th:block th:fragment="customHeader(title,keywords)">
  4.     <head>
  5.         <meta charset="UTF-8">
  6.         <title th:text="${title}"></title>
  7.         <meta name="keywords" th:content="${keywords}" />
  8.     </head>
  9. </th:block>
  10. <body>
  11. <th:block th:fragment="commonFooter">
  12.     <footer>这里是页脚<br />然后是一堆备案、版权的文字</footer>
  13. </th:block>
  14. </body>
  15. </html>

这个“iframe.html”无需定义任何路由,因为它只是用来存放一些模板控件。

然后,我们把greeting.html”改成引用这个文件的head和footer:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <th:block th:insert="iframe::customHeader('你好','关键词1,关键词2')"></th:block>
  4. <body>
  5. <p th:insert="lucky::#someEle"></p>
  6. <p th:replace="lucky::#someEle"></p>
  7. <p th:include="lucky::#someEle"></p>
  8. <th:block th:insert="iframe::commonFooter"></th:block>
  9. </body>
  10. </html>

访问“http://localhost:8080/jiaocheng/greeting”,得到的结果如下:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>你好</title>
  6. <meta name="keywords" content="关键词1,关键词2" />
  7. </head>
  8. <body>
  9. <p><p id="someEle">我是一个模板文本框</p></p>
  10. <p id="someEle">我是一个模板文本框</p>
  11. <p>我是一个模板文本框</p>
  12. <footer>这里是页脚<br />然后是一堆备案、版权的文字</footer>
  13. </body>
  14. </html>

可以看到,页面标题和关键词已经被替换成了带入的文本。

你也可以在后端定义变量。例如,把greeting.html”的head引用改为如下代码:

  1. <th:block th:insert="iframe::customHeader(${title},${keyword1}+','+${keyword2})"></th:block>

然后在“greeting”路由中,加入如下绑定:

  1. mv.addObject("title","标题:你好");
  2. mv.addObject("keyword1","天空");
  3. mv.addObject("keyword2","大海");

重新运行项目后,访问“http://localhost:8080/jiaocheng/greeting”,head部分会变成后端定义的值:

  1. <head>
  2. <meta charset="UTF-8">
  3. <title>标题:你好</title>
  4. <meta name="keywords" content="天空,大海" />
  5. </head>
注意:本页面内容为W3xue原创,未经授权禁止转载,违者必究!
来源:W3xue  发布时间:2019/9/29 12:31:24
 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号