由于之前项目是Springboot+VUE,但是VUE对于IE8及以下的浏览器存在兼容问题,所以最近可能需要重新做前端项目,不再前后端分离,而是在springboot里全部完成,由于springboot2.0以上已不再支持jsp文件,所以使用HTML+JQuery来重写前端,后端也使用springboot返回页面.但是对于页面布局.嵌套.异步刷新等问题不太熟悉.最近又重新学了一下thymleaf,这里准备做个记录.
提示:在IDEA中,开发页面时,可以通过ctrl+F9来重新build页面部分内容,无须重启项目来查看静态文件编辑效果
添加依赖
在Springboot中添加thymleaf和layout的依赖,在pom.xml中添加下面两个
<!-- thymleaf模板依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/nz.net.ultraq.thymeleaf/thymeleaf-layout-dialect -->
<dependency>
<groupId>nz.net.ultraq.thymeleaf</groupId>
<artifactId>thymeleaf-layout-dialect</artifactId>
</dependency>
第二个不确定需不需要,但是看大家都加了.所以也加上了!
之后就可以在Springboot项目中使用thymleaf了
Thymleaf的使用
这部分就参考thymleaf的各种文档吧,常用的就是th:text,th:value,th:each,th:if等等,很好懂.
因为只是尝试做简单的页面测试使用,所以使用IDEA画的HTML页面,在使用th:时,会提示我引入,但是看别的项目不写也能用
在文件头加上这句话
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
就可以使用th了
th:fragment使用
使用th:fragment定义一个模块,该模块可以在待使用的html文件中,也可以在其他html文件中.被th:fragment定义过的模块可以在其他地方通过th:include,th:insert,th:replace来引入进来
三种方式的区别:
th:insert :保留自己的主标签,保留th:fragment的主标签。
th:replace :不要自己的主标签,保留th:fragment的主标签。
th:include :保留自己的主标签,不要th:fragment的主标签。(官方3.0后不推荐)
举个栗子!
我定义一个mainModule.html文件,在里面写fragment的模块
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div th:fragment="head">
这里是modules的head fragment
</div>
<div th:fragment="left">
这里是modules的left fragment
</div>
<div th:fragment="right">
这里是modules的right fragment
</div>
<div th:fragment="foot">
这里是modules的foot fragment
</div>
</body>
</html>
定义了四个模块,head,left,right,foot.
之后新建另一个main.html文件,在其中使用th:include="/目录/文件名::模块名"来引入这个模块,也有使用"~{目录/文件名::模块名}"来引入的.实测都可行
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml" xmlns:layout="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Main</title>
</head>
<body>
<div>
<p>这里是主页面</p>
</div>
<div id="head" th:include="/modules/mainModule::head">这里是主页面的头部,包含的modules的一个文件里的头模块</div>
<div th:include="/modules/mainModule::left">这里是主页面的头部,包含的modules的一个文件里的左模块</div>
<div th:include="/modules/mainModule::right">这里是主页面的头部,包含的modules的一个文件里的右模块</div>
<div th:include="/modules/mainModule::foot">这里是主页面的头部,包含的modules的一个文件里的jio模块</div>
</body>
</html>
两个文件的目录结构如下:
在controller控制器中写一个main.html的接口
@RequestMapping("/main")
public ModelAndView main(){
ModelAndView modelAndView=new ModelAndView();
modelAndView.setViewName("/tempage/main");
return modelAndView;
}
启动项目,通过路径访问就可以看到刚刚写的页面
可以看到定义的4个DIV中显示的内容是我们在mainModule.html中定义的fragment的内容而不是main.html中的内容.
但是这种写法有个弊端,就是对于fragment模块无法进行动态的初始化,我之前试过写一个返回模块的接口,但是在include时依然只是单纯引入,不会调用接口,所以这种方法比较实适用于头,脚等规定模块的引入.
实现切换
在刚才的main.html中定义两个button,同时为它们添加两个点击事件
<button id="switchHead" onclick="changeHead()">切换头部</button>
<button id="switchHead2" onclick="changeHead2()">再切一次</button>
script中添加函数
<script src="http://libs.baidu.com/jquery/1.8.3/jquery.min.js"></script>
<script>
function changeHead(){
$.ajax({
url:"/page/headModule",
type:"post",
// data:{"param":"2"},
// dataType:"json",
success: function (data) {
$("#head").html(data);
}
});
}
function changeHead2(){
$.ajax({
url:"/page/leftModule",
type:"post",
// data:{"param":"2"},
// dataType:"json",
success: function (data) {
$("#head").html(data);
}
});
}
</script>
其中这两个函数的url是controller中定义的返回head模块和left模块的接口
@RequestMapping("/headModule")
public ModelAndView headModule(){
ModelAndView modelAndView=new ModelAndView();
modelAndView.setViewName("/modules/mainModule::head");
return modelAndView;
}
@RequestMapping("/leftModule")
public ModelAndView leftModule(){
ModelAndView modelAndView=new ModelAndView();
modelAndView.setViewName("/modules/mainModule::left");
return modelAndView;
}
这里需要注意的是,springboot返回模块的方式是 “/目录/文件名::模块名”,这两个接口返回的就是我们刚刚在mainModule.html文件中定义的head和left.
这样,当我们在页面中点击两个按钮时,我们id为head的这个DIV就会被替换为接口返回的模块了.
录屏不好录,不展示了
Layout布局使用
适用layout:fragment可以实现动态切换layout部分的功能
首先在要被替换的部分创建一个DIV <div layout:fragment="content"></div>
,之后在其他文件中定义这个DIV的具体内容,可以理解为声明和重写吧~
举个栗子:
在刚才的main.html中定义一个DIV
<div layout:fragment="content">这里是分页模块</div>
在另一个文件page.html中重写这个DIV
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml" layout:decorator="tempage/main" xmlns:layout="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div layout:fragment="content">
<p>这是fragment中的contentdiv</p>
<button onclick="changePage()">点这里翻页!</button>
<input type="text" id="pageNum"/>
<div id="pageFragment" style="background-color: #00e765" th:fragment="pageFragment">
<div>
<p>这是pageDiv里的Div,显示数据的</p>
<ul th:each="pageDetail:${pageList.records}">
<li th:text="${pageDetail.title}">模板数据</li>
</ul>
</div>
</div>
</div>
</body>
</html>
在controller中添加这个文件的接口
@RequestMapping("/pageLayout")
public ModelAndView pageLayout(HttpServletRequest request){
//获取参数
String pageStr=request.getParameter("page");
Integer page=1;
if(Util.isNotEmpty(pageStr)){
page=Integer.parseInt(pageStr);
}
//查询数据
KjTalentActivityQuery query=new KjTalentActivityQuery();
query.setPage(page);
query.setLimit(10);
Page<KjTalentActivityEntity> activityPage=pageService.queryActivity(query);
//封装页面
ModelAndView modelAndView=new ModelAndView();
modelAndView.setViewName("/fragment/page");
modelAndView.addObject("pageList",activityPage);
return modelAndView;
}
相当于这个页面的初始化,在调用这个接口时,根据参数查询数据库,得到返回值,封装为pageList属性,返回给页面,在页面中通过th:each遍历显示出来.
启动项目,当我们访问main时,显示的是div中默认的文字,如下图:
当我们更换路径,将路径换成刚才写的page的路径时,就会看到分页模块被替换成了page.html中的内容
当我们切换路径,比如传参数更换查询的页数时,绿色这个模块也会跟着改变.
这种方法的缺陷是,这种切换方式是全局刷新,如果其他地方我们做了样式变化,如果不使用参数进行限制,会被重置为默认状态.但是作为导航栏切换来说还算是很方便的
Layout和Thymleaf实现分页异步刷新
Layout解决了布局问题,th:fragment解决了局部刷新问题,两个联合在一起就可以解决我们被layout包含这个模块的异步刷新问题.
继续刚才的main.html和page.html
刚才的page.html中,我做了一个input框来输入页码,一个button来触发ajax分页异步刷新,显示数据的div定义为了一个th:fragment="pageFragment"
js部分代码如下:
<script src="http://libs.baidu.com/jquery/1.8.3/jquery.min.js"></script>
<script>
function changePage(){
var pageNum=$("#pageNum").val();
alert(pageNum);
$.ajax({
url:"/page/pageModule",
type:"post",
data:{"page":pageNum},
dataType:"html",
success: function (data) {
console.log(data);
$("#pageFragment").html(data);
},
error:function(data){
console.log("error:+"+data);
}
});
}
</script>
请求controller返回这个fragment的接口,通过ajax实现局部刷新
controller:
@RequestMapping("/pageModule")
public ModelAndView pageModule(HttpServletRequest request){
//获取参数
String pageStr=request.getParameter("page");
Integer page=1;
if(Util.isNotEmpty(pageStr)){
page=Integer.parseInt(pageStr);
}
//查询数据
KjTalentActivityQuery query=new KjTalentActivityQuery();
query.setPage(page);
query.setLimit(10);
Page<KjTalentActivityEntity> activityPage=pageService.queryActivity(query);
//封装页面
ModelAndView modelAndView=new ModelAndView();
modelAndView.setViewName("/fragment/page::pageFragment");
modelAndView.addObject("pageList",activityPage);
return modelAndView;
}
这个接口返回的是一整个pageFragment,将这个fragment使用html()函数直接替换掉fragment所在的DIV就可以了
在network中可以看到这个ajax请求的返回值
通过success中的$("#pageFragment").html(data);
实现异步刷新.
录屏不好录,不录了.就是普通的ajax异步刷新效果!
能直接返回模块的好处是避免在ajax中拼接标签+数据,也避免在后台写,也算是标签的可复用吧hhhhhh
大概就是这个样子了.具体实际应用再遇到问题再来补充.