• 中文
    • English
  • 注册
  • 查看作者
    • SpringBoot:关于thymeleaf的简单使用

      一.  数据准备

      1.  想要使用thymeleaf,需要先在pom.xml中添加相关依赖

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-thymeleaf</artifactId>
      </dependency>

      2. 接下来准备再一个简单的页面index.html,在resources下的templates文件下,新建index.html

      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>Title</title>
      </head>
      <body>
      <h1>我是孙著杰的同桌</h1>
      </body>
      </html>

      3.  最后编写Controller

      package io.zhangjia.springboot3.controller;
      
      import org.springframework.stereotype.Controller;
      import org.springframework.web.bind.annotation.GetMapping;
      
      @Controller
      public class WebController {
      
          @GetMapping("/index")
          public String index(){
              return "index";
          }
      }

      4.  接下来访问http://localhost:8888/index,即可显示:我是孙著杰的同桌,可能细心的同学已经注意到,我们并没有配置视图解析器为return “index”添加前缀和后缀,但是为什么依旧能够成功访问index.html呢?原因是因为SpringBoot已经默认为我们配置了视图解析器,打开External中的Maven:org.springframework.boot:spring-boot-autoconfigure:2.1.6.RELEASE,找到thymeleaf—>ThymeleafAutoConfiguration,点击其中ThymeleafProperties.class,即可看到以下内容:

      @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 cache;
          ....
      }

      所以我们只需要将html页面放在类路径的templates文件下,页面便可正常被转发。

      5.  接下来再次修改html页面, 添加以下内容:

      <h1>孙著杰是我的同桌</h1>

      右击选择Recompile ' index. html' (快捷键 Ctrl+Shift+F9,相当于之前的packge),此时回到http://localhost:8888/index,并刷新页面,新添加的h1标签的内容却没有显示,这是因为缓存默认被关闭,在配置文件中将其开启即可:

      spring:
        datasource:
          driver-class-name: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://localhost:3306/demo2?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
          username: root
          password: zhangjia
          type: com.alibaba.druid.pool.DruidDataSource
        thymeleaf:
          cache: false

      三.  th的简单使用

      修改Controller,添加某个数据到Request作用域

      @Controller
      public class WebController {
      
          @GetMapping("/index")
          public String index(Model model){
              String name = "zhangjia";
              model.addAttribute("name",name);
              return "index";
          }
      }

      因为此时的视图页面不再是.jsp,而是.html,所以我们可以使用thymeleaf来显示数据,使用thymeleaf可以实现静动分离,举个简单的例子,在之前的项目练习中,我们都是先从网上找到相应的前端模板,然后将其后缀从html修改为jsp,假如该模板中有一个h2标签用于显示用户姓名:

      <h2>姓名</h2>

      则我们需要先将姓名而在删除,将其修改为:

      <h2>${name}</h2>

      而如果使用thymeleaf,我们便可以不删除姓名,而是采用下面的方式:

      <!DOCTYPE html>
      <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
      <head>
          <meta charset="UTF-8">
          <title>Title</title>
      </head>
      <body>
      <h2 th:text="${name}">姓名</h2>
      </body>
      </html>

      此时访问http://localhost:8888/index,页面却不是姓名二字,而是显示zhangjia

      同理,如果是其他属性,也可以直接在th后面修改,比如修改input:

      <input th:type="password" placeholder="请输入密码" th:class="jia-input"/>

      但是值得注意的是,a标签的href,或者图片的src属性等在thymeleaf中不能通过上述方式直接使用th编写,而需要使用@{xxx}将地址包裹:

      <a href="#" th:href="@{https://www.google.com/}">谷歌</a>

      同理,css、js等文件的引入如果使用的是在线资源, 是不需要动态获取的,但是如果使用的是本地资源,则必须动态获取

      <head>
          <meta charset="UTF-8">
          <title>Title</title>
          <link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/4.1.0/css/bootstrap.min.css">
          <script src="https://cdn.staticfile.org/jquery/3.2.1/jquery.min.js"></script>
      <!--如果使用本地的css文件,则这样写:
      <link rel="stylesheet" href=".../static/css/bootstrap.min.css"> 只这样写,是不能动态获取的,只有静态获取才能成功
      需要改成下面的样式
      <link rel="stylesheet" th:href="@{css/bootstrap.min.css}" href=".../static/css/bootstrap.min.css">
      -->
      </head>

      注意,动态获取的时候资源地址不需要添加../static/,原因如下:

      我们可以打开External中的Maven:org.springframework.boot:spring-boot-autoconfigure:2.1.6.RELEASE,找到web—>Servlet—>ResourceProperties,即可看到以下内容:

      private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"};

      也就是说,我们的静态资源的存放位置有上面的四种方式,所以在动态获取的时候,不需要再添加../static

      三.  循环迭代

      假如我们有以下内容的JSON字符串

      [{"address":{"create_time":1563861382000,"user_id":4001,"name":"张三","tel":"13666666666","addr":"山东省济宁市","is_default":1,"aid":6001,"status":1},"create_time":1563931245000,"total_price":19964.0,"user_id":4001,"order_id":7001,"status":1,"products":[{"quantity":2,"img_url":"59ec3325N906f107e.jpg","price":8588.0,"dbid":8001,"product_id":3010,"name":"Apple 苹果 iPhone X 全面屏手机 深空灰色 全网通 64GB","order_id":7001},{"quantity":1,"img_url":"5abb0fe7N5f2b7ed2.jpg","price":2788.0,"dbid":8002,"product_id":3022,"name":"Apple iPad 平板电脑 2018年新款9.7英寸(128G WLAN版/A10芯片/Retina屏 MR7J2CH/A)深空灰色","order_id":7001}]},{"address":{"create_time":1563861427000,"user_id":4001,"name":"李四","tel":"13888889999","addr":"山东省青岛市","is_default":0,"aid":6002,"status":1},"create_time":1563935813000,"total_price":2599.0,"user_id":4001,"order_id":7002,"status":1,"products":[{"quantity":1,"img_url":"597ae1efNc6dd8fe4.jpg","price":2599.0,"dbid":8003,"product_id":3012,"name":"小米6 全网通 6GB+128GB 陶瓷黑尊享版 移动联通电信4G手机 双卡双待","order_id":7002}]}]

      将该json字符串格式化后显示如下:

      [
          {
              "address":{
                  "create_time":1563861382000,
                  "user_id":4001,
                  "name":"张三",
                  "tel":"13666666666",
                  "addr":"山东省济宁市",
                  "is_default":1,
                  "aid":6001,
                  "status":1
              },
              "create_time":1563931245000,
              "total_price":19964,
              "user_id":4001,
              "order_id":7001,
              "status":1,
              "products":[
                  {
                      "quantity":2,
                      "img_url":"59ec3325N906f107e.jpg",
                      "price":8588,
                      "dbid":8001,
                      "product_id":3010,
                      "name":"Apple 苹果 iPhone X 全面屏手机 深空灰色 全网通 64GB",
                      "order_id":7001
                  },
                  {
                      "quantity":1,
                      "img_url":"5abb0fe7N5f2b7ed2.jpg",
                      "price":2788,
                      "dbid":8002,
                      "product_id":3022,
                      "name":"Apple iPad 平板电脑 2018年新款9.7英寸(128G WLAN版/A10芯片/Retina屏 MR7J2CH/A)深空灰色",
                      "order_id":7001
                  }
              ]
          },
          {
              "address":{
                  "create_time":1563861427000,
                  "user_id":4001,
                  "name":"李四",
                  "tel":"13888889999",
                  "addr":"山东省青岛市",
                  "is_default":0,
                  "aid":6002,
                  "status":1
              },
              "create_time":1563935813000,
              "total_price":2599,
              "user_id":4001,
              "order_id":7002,
              "status":1,
              "products":[
                  {
                      "quantity":1,
                      "img_url":"597ae1efNc6dd8fe4.jpg",
                      "price":2599,
                      "dbid":8003,
                      "product_id":3012,
                      "name":"小米6 全网通 6GB+128GB 陶瓷黑尊享版 移动联通电信4G手机 双卡双待",
                      "order_id":7002
                  }
              ]
          }
      ]

      可以看到该JSON字符串中存放了一个用户的两个订单信息,其中第一个订单中有两个商品,第二个订单中有一个商品,接下来在Controller中将字符串转换为Java对象,并将其存入request作用域,并在视图中显示相关的数据,如果是JSP页面,一般我们会采用JSTL和EL来遍历处理这类数据,而thymeleaf也提供了相应的循环遍历的语法,即:th:each:

       <div th:each="order : ${orders}" class="card">
                      <div class="card-header">
            <!--           方法一:  -->
      <!--   <span th:text="${'下单时间:' + order.create_time}">下单时间:2019-6-7 16:13:28</span>-->
           <!--           方法二:  -->
                         <span th:text="${'下单时间:' + #dates.format(order.create_time, 'yyyy-MM-dd sHH:mm:ss')}">下单时间:2019-6-7 16:13:28</span>
                          <span th:text="${'订单号:' + order.order_id}">订单号:14</span>
                      </div>
                      <div class="card-body">
                          <table class="table table-sm table-bordered">
                              <tr th:each="product,status : ${order.products}">
                                  <td class="w-100px">
                                      <img class="img-thumbnail" width="70px" height="70px" src="http://iph.href.lu/80x80?text=1" alt="">
                                  </td>
                                  <td th:text="${product.name}">
                                      Apple iPhone XR (A2108) 128GB 黑色 移动联通电信4G手机 双卡双待
                                  </td>
                                  <td class="w-50px text-center" th:text="${product.quantity}">1</td>
                                  <td class="w-100px text-center" th:text="${#numbers.formatCurrency(product.price)}">¥6699.00</td>
                                  <td class="w-120px" th:if="${status.index == 0}" th:rowspan="${status.size}" rowspan="2" >
      
                                      <span th:text="${order.address.name}">张三<br></span>
      
                                      <span th:text="${order.address.tel}">186XXXX2865<br></span>
      
                                      <span th:text="${order.address.addr}">山东省济宁市高新区XX路XX大厦XXX室</span>
                                  </td>
                                  <td class="w-120px" th:if="${status.index == 0}" th:rowspan="${status.size}" rowspan="2">
                                      总金额:<br>
                                      <span th:text="${#numbers.formatCurrency(order.total_price)}">¥31,495.00</span>
                                  </td>
                              </tr>
      
                          </table>
                      </div>
                  </div>

      在上⾯代码中,th:each=”order : ${orders}” 的功能其实类似于foreach:即for(var order: orders) ,也就是说orders是被迭代的变量,而order为迭代变量,orders中存放了两个订单中的所有数据,而每个order中存放的都是一个订单的数据。

      同理,<tr th:each=”product,status : ${order.products}”> 中的order.products则存放是的一个订单中的所有商品,而product则是每个商品的数据。

      另外,product后面的status存放的是迭代相关的信息:

      • index:第几次循环,下标从0开始

      • count:第几次循环,下标从1开始

      • size:一共循环几次

      • current:当前正在循环的迭代变量

      另外th:if=”${status.index == 0}”相当于判断,如果status.index == 0,则会显示该标签

      四.  引入页面中公共部分

      假如我们的网站中有一百个页面,这一百个页面肯定会有一些公共的部分内容是相同的,比如菜单、网站底部信息等等,在jsp中我们可以使用静态导入或者动态导入的方法来解决这个问题,相应的,thymeleaf也为我们提供了插入或者替换两种方式来将页面中的公共部分引入

      这里以任意界面的菜单为例:

      <!DOCTYPE html>
      <html lang="en" xmlns:th="http://www.thymeleaf.org">
      <head>
          <meta charset="UTF-8">
          <title>首页 - 电子商城</title>  
      
      </head>
      <body>
      <header >
          <div class="container">
              <nav class="navbar navbar-expand-lg navbar-light bg-light rounded">
                  <a class="navbar-brand" href="#">电子商城1</a>
                  <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExample09" aria-controls="navbarsExample09" aria-expanded="false" aria-label="Toggle navigation">
                      <span class="navbar-toggler-icon"></span>
                  </button>
      
                  <div class="collapse navbar-collapse" id="navbarsExample09">
                      <ul class="navbar-nav mr-auto">
                          <li class="nav-item active">
                              <a class="nav-link" href="#">首页<span class="sr-only">(current)</span></a>
                          </li>
                          <li class="nav-item">
                              <a class="nav-link" href="#">电脑</a>
                          </li>
                          <li class="nav-item">
                              <a class="nav-link" href="#">手机</a>
                          </li>
                          <li class="nav-item">
                              <a class="nav-link" href="#">平板</a>
                          </li>
                          <li class="nav-item dropdown">
                              <a class="nav-link dropdown-toggle" href="#" id="dropdown09" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">张小三</a>
                              <div class="dropdown-menu" aria-labelledby="dropdown09">
                                  <a class="dropdown-item" href="#">个人中心</a>
                                  <a class="dropdown-item" href="#">购物车</a>
                                  <a class="dropdown-item" href="#">收货地址</a>
                                  <a class="dropdown-item" href="#">我的订单</a>
                                  <a class="dropdown-item" href="#">退出登录</a>
                              </div>
                          </li>
                      </ul>
                      <form class="form-inline my-2 my-md-0">
                          <input class="form-control" type="text" placeholder="Search" aria-label="Search">
                          <input type="submit" class="btn btn-outline-secondary" value="搜索">
                      </form>
                  </div>
              </nav>
          </div>
      
      </header>
      <main role="main" class="container">
          ......
      </main>
      <footer class="fdb-block footer-small">
        ......
      </footer>
      </body>
      </html>

      在上述代码中,<header>标签的代码即为网站的菜单,也就是每个页面都会拥有的公平部分,我们可以新建一个专门用于存放公共部分的页面的_common.html,然后将header标签中的内容存入:

      _common.html:

      <header th:fragment="head">
          <div class="container">
              <nav class="navbar navbar-expand-lg navbar-light bg-light rounded">
                  <a class="navbar-brand" href="#">电子商城</a>
                  <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExample09" aria-controls="navbarsExample09" aria-expanded="false" aria-label="Toggle navigation">
                      <span class="navbar-toggler-icon"></span>
                  </button>
      
                  <div class="collapse navbar-collapse" id="navbarsExample09">
                      <ul class="navbar-nav mr-auto">
                          <li class="nav-item active">
                              <a class="nav-link" href="#">首页<span class="sr-only">(current)</span></a>
                          </li>
                          <li class="nav-item">
                              <a class="nav-link" href="#">电脑</a>
                          </li>
                          <li class="nav-item">
                              <a class="nav-link" href="#">手机</a>
                          </li>
                          <li class="nav-item">
                              <a class="nav-link" href="#">平板</a>
                          </li>
                          <li class="nav-item dropdown">
                              <a class="nav-link dropdown-toggle" href="#" id="dropdown09" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">张小三</a>
                              <div class="dropdown-menu" aria-labelledby="dropdown09">
                                  <a class="dropdown-item" href="#">个人中心</a>
                                  <a class="dropdown-item" href="#">购物车</a>
                                  <a class="dropdown-item" href="#">收货地址</a>
                                  <a class="dropdown-item" href="#">我的订单</a>
                                  <a class="dropdown-item" href="#">退出登录</a>
                              </div>
                          </li>
                      </ul>
                      <form class="form-inline my-2 my-md-0">
                          <input class="form-control" type="text" placeholder="Search" aria-label="Search">
                          <input type="submit" class="btn btn-outline-secondary" value="搜索">
                      </form>
                  </div>
              </nav>
          </div>
      </header>

      其中th:fragment=”head” 相当于为该部分代码起了一个别名,用来唯一标识。同样的,如果需要定义页面中的其他公共部分,只需要在_common.html中添加多个th:fragment=”xxx”即可,无需为每公共部分单独定义一个文件。

      接下来演示使用插入和替换两种方式在每个页面使用这些公共部分

      1.  插入

      <header  th:insert="_common :: head">
      <!--   header中的原内容也可以不删除-->
      </header>

      2.  替换

      <header th:replace="_common :: head">
      <!--   header中的原内容也可以不删除-->
      </header>

      其中_common即为_common.html的文件名,而head即为要导入的公平部分的唯一标识。

      另外,如果采用插入的方式,那么在_common.html中,需要将

      <header th:fragment="head">
          <div class="container">
          ....
          </div>
      </header>
      修改为:
      <div th:fragment="head" class="container">
          ....
      </div>

      这样的做的原因是因为插入会将_common.html中的代码原封不等的插入进header标签内,所以如果不修改,则页面的代码会变成:

      <header >
          <header>   
              <div class="container">   
                  .....
              </div>   
          </header>
      </header>

      而替换则不需要考虑上述问题。另外还有th:include这种导入方式,但是官方建议在3.0之后不推荐使用

    • 0
    • 0
    • 0
    • 3.5k
    • 请登录之后再进行评论

      登录

      赞助本站

      • 支付宝
      • 微信
      • QQ

      感谢一直支持本站的所有人!

      单栏布局 侧栏位置: