由于之前项目是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

大概就是这个样子了.具体实际应用再遇到问题再来补充.

Logo

前往低代码交流专区

更多推荐