模板引擎
前端交给我们的页面,是html页面。如果是我们以前开发,我们需要把他们转成jsp页面,jsp好处就是当我们查出一些数据转发到JSP页面以后,我们可以用jsp轻松实现数据的显示,及交互等。
jsp支持非常强大的功能,包括能写Java代码,但是呢,我们现在的这种情况,SpringBoot这个项目首先是以jar的方式,不是war,像第二,我们用的还是嵌入式的Tomcat,所以呢,他现在默认是不支持jsp的。
那不支持jsp,如果我们直接用纯静态页面的方式,那给我们开发会带来非常大的麻烦,那怎么办呢?
SpringBoot推荐你可以来使用模板引擎:
模板引擎,我们其实大家听到很多,其实jsp就是一个模板引擎,还有用的比较多的freemarker,包括SpringBoot给我们推荐的Thymeleaf,模板引擎有非常多,但再多的模板引擎,他们的思想都是一样的,什么样一个思想呢我们来看一下这张图:
版本引擎的作用: 把这个模板和这个数据交给我们模板引擎,模板引擎按照我们这个数据帮你把这表达式解析、填充到我们指定的位置,然后把这个数据最终生成一个我们想要的内容给我们写出去,这就是我们这个模板引擎,不管是jsp还是其他模板引擎,都是这个思想。只不过呢,就是说不同模板引擎之间,他们可能这个语法有点不一样。
引入Thymeleaf
怎么引入呢,对于 springboot 来说,什么事情不都是一个start的事情嘛,我们去在项目中引入一下。给大家三个网址:
- Thymeleaf 官网:https://www.thymeleaf.org/
- Thymeleaf 在Github 的主页:https://github.com/thymeleaf/thymeleaf
- Spring官方文档:找到我们对应的版本
https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/reference/htmlsingle/#using-boot-starter
找到对应的pom依赖:可以适当点进源码看下本来的包!
<!--thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
Maven会自动下载jar包,我们可以去看下下载的东西;
Thymeleaf 的使用
我们首先得按照 SpringBoot 的自动配置原理看一下我们这个Thymeleaf的自动配置规则,在按照那个规则,我们进行使用。
我们去找一下 Thymeleaf 的自动配置类:ThymeleafProperties
@ConfigurationProperties(
prefix = "spring.thymeleaf"
)
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING;
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
private boolean checkTemplate = true;
private boolean checkTemplateLocation = true;
private String prefix = "classpath:/templates/";
private String suffix = ".html";
private String mode = "HTML";
private Charset encoding;
}
我们可以在其中看到默认的前缀和后缀!
因此,我们只需要把我们的html页面放在类路径下的templates下,thymeleaf就可以帮我们自动渲染了。
测试
-
1、编写一个TestController
@Controller public class TestController { @RequestMapping("/test") public String test(){ return "test"; } }
-
2、编写一个测试页面 test.html 放在 templates 目录下
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> 首页 </body> </html>
-
3、启动项目请求测试
小结: -
只要需要使用 thymeleaf ,只需要导入对应的依赖就可以了,我们需要将 html 页面放在我们的 templates 目录下即可。
Thymeleaf 语法学习
要学习语法,还是参考官网文档最为准确,我们找到对应的版本看一下;
- Thymeleaf 官网:https://www.thymeleaf.org/ , 简单看一下官网!我们去下载Thymeleaf的官方文档!
注意: 要使用 thymeleaf,需要在 html 文件中导入命名空间的约束。
xmlns:th="http://www.thymeleaf.org"
语法规则
html 任意属性
th:任意html属性;来替换原生属性的值
变量表达式($)
变量表达式即 OGNL 表达式或Spring EL表达式(在Spring术语中也叫model attributes)。如下所示:${session.user.name}
它们将以HTML标签的一个属性来表示:
<span th:text="${book.author.name}">
<li th:each="book : ${books}">
选择(星号)表达式(*)
选择表达式很像变量表达式,不过它们用一个预先选择的对象来代替上下文变量容器(map)来执行
简单的来说,就是在一个作用域类,可以直接用 选择表达来表示对象中的某个属性,如下:
<div th:object="${book}">
...
<span th:text="*{title}">...</span>
...
</div>
文字国际化表达式(#)
文字国际化表达式允许我们从一个外部文件获取区域文字信息(.properties),用Key索引Value,还可以提供一组参数(可选).
#{main.title}
#{message.entrycreated(${entryId})}
可以在模板文件中找到这样的表达式代码:
<table>
...
<th th:text="#{header.address.city}">...</th>
<th th:text="#{header.address.country}">...</th>
...
</table>
URL表达式(@)
URL表达式指的是把一个有用的上下文或回话信息添加到URL,这个过程经常被叫做URL重写。如下:
@{/order/list}
URL还可以设置参数:
@{/order/details(id=${orderId})}
相对路径:
@{../documents/report}
让我们看这些表达式:
<form th:action="@{/createOrder}">
<a href="main.html" th:href="@{/main}">
常用th标签
Text(tag body modification【标签体改造】)
关键字 | 功能介绍 | 案例 |
---|---|---|
th:text | 文本替换 | <p th:text="${collect.description}">description</p> |
th:utext | 支持html的文本替换 | <p th:utext="${htmlcontent}">conten</p> |
-
测试 如下:
@Controller public class IndexController { @RequestMapping("/test") public String test(Model model){ model.addAttribute("msg","<h1>hello springboot</h1>"); return "test"; } }
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div th:text="${msg}"></div> <div th:utext="${msg}"></div> </body> </html>
运行项目,访问localhost:8080/test
Fragment inclusion(片段的包含)
关键字 | 功能介绍 | 案例 |
---|---|---|
th:include | 布局标签,替换内容到引入的文件 | <head th:include="layout :: htmlhead" th:with="title='xx'"></head> /> |
Fragment iteration(片段的迭代)
关键字 | 功能介绍 | 案例 |
---|---|---|
th:each | 属性赋值 | <tr th:each="user,userStat:${users}"> |
th:each | 属性取值 | <tr th:each="user:${users}" th:text="${user}"></tr> |
th:each 属性取值 :
model.addAttribute("users", Arrays.asList("张三","李四"));
<!--建议写法-->
<h3 th:each="user:${users}" th:text="${user}"></h3>
<!--同效果行内写法-->
<h3 th:each="user:${users}">[[${user}]]</h3>
Condition evaluation(条件评估)
关键字 | 功能介绍 | 案例 |
---|---|---|
th:if | 判断条件 | <a th:if="${userId == collect.userId}" > |
th:unless | 和th:if判断相反 | <a th:href="@{/login}"th:unless=${session.user != null}>Login</a> |
th:switch | 多路选择 | 配合th:case 使用 <div th:switch="${user.role}"> |
th:case | th:switch的一个分支 | <p th:case="'admin'">User is an administrator</p> |
Local variable definition(局部变量的定义)
关键字 | 功能介绍 | 案例 |
---|---|---|
th:object | 替换对象 | <div th:object="${session.user}"> |
th:with | 变量赋值运算 | <div th:with="isEven=${prodStat.count}%2==0"></div> |
General attribute modification(通用属性修改)
关键字 | 功能介绍 | 案例 |
---|---|---|
th:attr | 设置标签属性,多个属性可以用逗号分隔 | 比如 th:attr="src=@{/image/aa.jpg},title=#{logo}" ,此标签不太优雅,一般用的比较少。 |
th:attrappend | 将求值的结果附加(后缀)到现有属性值。 | <input type="button" value="Do it!" class="btn" th:attrappend="class=${' ' + cssStyle}" /> |
th:attrprepend | 将求值的结果附加(前缀)到现有属性值。 | <input type="button" value="Do it!" class="btn" th:attrprepend="class=${' ' + cssStyle}" /> |
Specific attribute modification(特定属性修改)
关键字 | 功能介绍 | 案例 |
---|---|---|
th:value | 属性赋值 | <input th:value="${user.name}" /> |
th:href | 链接地址 | <a th:href="@{/login}" th:unless=${session.user != null}>Login</a> /> |
th:src | 图片类地址引入 | <img class="img-responsive" alt="App Logo" th:src="@{/img/logo.png}" /> |
Fragment specification(片段规范)
关键字 | 功能介绍 | 案例 |
---|---|---|
th:fragment | 布局标签,定义一个代码片段,方便其它地方引用 | <div th:fragment="alert"> |
Fragment removal(片段删除)
关键字 | 功能介绍 | 案例 |
---|---|---|
th:remove | 删除某个属性 | <tr th:remove="all"> 1.all:删除包含标签和所有的孩子。 |
Web 实验
官方文档 - Template Layout<>
登录模块
1、创建静态资源和所需要的html
- /static 放置 css,js等静态资源
/templates/login.html
登录页 。/templates/main.html
主页,作为登录后跳转的页面
注意:templates
文件夹里面的所有文件必须通过 controller 才能够访问。
2、编写 User
@AllArgsConstructor
@NoArgsConstructor
@Data
public class User {
private String userName;
private String password;
}
3、编写 login html。
-
利用 模板引擎完成 登录提交的action
-
登录页面的核心代码如下:
<html lang="en" xmlns:th="http://www.thymeleaf.org"><!-- 要加这玩意thymeleaf才能用 --> <!--表单以post的方式提交,即按照Rest风格,如果是post的“/login”就为登录验证,其中“th:action”中的 “@{}”符号会自动为其添加url前缀--> <form class="form-signin" action="index.html" method="post" th:action="@{/login}"> ... <!-- 消息提醒 --> <label style="color: red" th:text="${msg}"></label> <input type="text" name="userName" class="form-control" placeholder="User ID" autofocus> <input type="password" name="password" class="form-control" placeholder="Password"> <button class="btn btn-lg btn-login btn-block" type="submit"> <i class="fa fa-check"></i> </button> ... </form>
4、编写main
main 首先会保存login的用户名,可以使用session来保存。下面为thymeleaf内联写法,显示用户名(下面这种为行内写法,即直接的纯文本,没有用标签把它给括起来):
5、编写控制层
@Controller
public class IndexController {
/**
* 来登录页
* @return
*/
@GetMapping(value = {"/","/login"})
public String loginPage(){
return "login";
}
@PostMapping("/login")
public String main(User user, HttpSession session, Model model){ //RedirectAttributes
if(StringUtils.hasLength(user.getUserName()) && "123456".equals(user.getPassword())){
//把登陆成功的用户保存起来
session.setAttribute("loginUser",user);
//登录成功重定向到main.html; 重定向防止表单重复提交
return "redirect:/main.html";
}else {
model.addAttribute("msg","账号密码错误");
//回到登录页面
return "login";
}
}
/**
* 去main页面
* @return
*/
@GetMapping("/main.html")
public String mainPage(HttpSession session, Model model){
//最好用拦截器,过滤器
Object loginUser = session.getAttribute("loginUser");
if(loginUser != null){
return "main";
}else {
//session过期,没有登陆过
//回到登录页面
model.addAttribute("msg","请重新登录");
return "login";
}
}
}
公共页面模块
-
公共页面的路径
/templates/common.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"><!--注意要添加xmlns:th才能添加thymeleaf的标签--> <head th:fragment="commonheader"> <!--common--> <link href="css/style.css" th:href="@{/css/style.css}" rel="stylesheet"> <link href="css/style-responsive.css" th:href="@{/css/style-responsive.css}" rel="stylesheet"> ... </head> <body> <!-- left side start--> <div id="leftmenu" class="left-side sticky-left-side"> ... <div class="left-side-inner"> ... <!--sidebar nav start--> <ul class="nav nav-pills nav-stacked custom-nav"> <li><a th:href="@{/main.html}"><i class="fa fa-home"></i> <span>Dashboard</span></a></li> ... <li class="menu-list nav-active"><a href="#"><i class="fa fa-th-list"></i> <span>Data Tables</span></a> <ul class="sub-menu-list"> <li><a th:href="@{/basic_table}"> Basic Table</a></li> <li><a th:href="@{/dynamic_table}"> Advanced Table</a></li> <li><a th:href="@{/responsive_table}"> Responsive Table</a></li> <li><a th:href="@{/editable_table}"> Edit Table</a></li> </ul> </li> ... </ul> <!--sidebar nav end--> </div> </div> <!-- left side end--> <!-- header section start--> <div th:fragment="headermenu" class="header-section"> <!--toggle button start--> <a class="toggle-btn"><i class="fa fa-bars"></i></a> <!--toggle button end--> ... </div> <!-- header section end--> <div id="commonscript"> <!-- Placed js at the end of the document so the pages load faster --> <script th:src="@{/js/jquery-1.10.2.min.js}"></script> <script th:src="@{/js/jquery-ui-1.9.2.custom.min.js}"></script> <script th:src="@{/js/jquery-migrate-1.2.1.min.js}"></script> <script th:src="@{/js/bootstrap.min.js}"></script> <script th:src="@{/js/modernizr.min.js}"></script> <script th:src="@{/js/jquery.nicescroll.js}"></script> <!--common scripts for all pages--> <script th:src="@{/js/scripts.js}"></script> </div> </body> </html>
-
/templates/table/basic_table.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"> <meta name="description" content=""> <meta name="author" content="ThemeBucket"> <link rel="shortcut icon" href="#" type="image/png"> <title>Basic Table</title> <div th:include="common :: commonheader"> </div><!--将common.html的代码段 插进来--> </head> <body class="sticky-header"> <section> <div th:replace="common :: #leftmenu"></div> <!-- main content start--> <div class="main-content" > <div th:replace="common :: headermenu"></div> ... </div> <!-- main content end--> </section> <!-- Placed js at the end of the document so the pages load faster --> <div th:replace="common :: #commonscript"></div> </body> </html>
-
th:include:
加载模板的内容: 读取加载节点的内容(不含节点名称),替换div内容 -
th:replace:
替换当前标签为模板中的标签,加载的节点会整个替换掉加载他的div
遍历数据与页面bug修改
-
控制层代码:
@GetMapping("/dynamic_table") public String dynamic_table(Model model){ //表格内容的遍历 List<User> users = Arrays.asList(new User("zhangsan", "123456"), new User("lisi", "123444"), new User("haha", "aaaaa"), new User("hehe ", "aaddd")); model.addAttribute("users",users); return "table/dynamic_table"; }
-
页面代码:
<table class="display table table-bordered" id="hidden-table-info"> <thead> <tr> <th>#</th> <th>用户名</th> <th>密码</th> </tr> </thead> <tbody> <tr class="gradeX" th:each="user,stats:${users}"> <td th:text="${stats.count}">Trident</td> <td th:text="${user.userName}">Internet</td> <td >[[${user.password}]]</td> </tr> </tbody> </table>