Thymeleaf模板引擎第一次使用,之前用过的都是freemarker,结合Spring boot记录一下初次使用时遇到的一些小坑,主要是路径和包名的一些问题。下面将记录使用thymeleaf的layout布局方式展现web内容的整个过程。
Build with Maven
Thymeleaf有两种布局的方式,第一种是使用 th:include 和 th:replace这种的形式,第二种是使用layout:decorate这种。
本文使用第二种,需要引入thymeleaf-layout-dialect这个包,否则layout:decorate这种布局方式不会生效(踩到的第一个坑)
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId> nz.net.ultraq.thymeleaf</groupId> <artifactId>thymeleaf-layout-dialect</artifactId> </dependency>
ViewModel
这里对html模板进行了一个封装,使其可以更加灵活方便地使用,就是一个POJO类,对Template中变动的内容进行定义,在提供一个返回ModelAndView对象的方法。
[mdx_fold title="WeuiResultPage.java"]package me.icharm.orange.ViewModel; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.apache.commons.beanutils.BeanUtils; import org.springframework.web.servlet.ModelAndView; import java.util.Map; /** * @author mylicharm * @email icharm.me@outlook.com * @date 2018/11/21 17:40 */ @Data @Slf4j public class WeuiResultPage { /** * 视图模板路径 */ public String path = "weui/result_fragment"; /** * 图标 */ private String icon; /** * 标题 */ private String title; /** * 内容 */ private String content; /** * 绿色按钮名称 */ private String btnPrimary; /** * 白色按钮名称 */ private String btnDefault; /** * 绿色按钮事件 */ private String btnPrimaryAction; /** * 白色按钮事件 */ private String btnDefaultAction; /** * This object convert to ModelAndView object. * * @return ModelMap */ public ModelAndView modelAndView() { try { ModelAndView mv = new ModelAndView(this.path); // Object to map Map<String, String> map = BeanUtils.describe(this); mv.addAllObjects(map); return mv; } catch (Exception e) { log.error("WeuiResultPage ViewModel Object convert to map error: " + e); return null; } } }[/mdx_fold]
Controller
Controller中很简单,new 一个WeuiResultPage对象,返回其对应的ModeAndView对象。
[mdx_fold title="CommonController.java"]
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; import me.icharm.orange.ViewModel.WeuiResultPage; /** * @author mylicharm * @email icharm.me@outlook.com * @date 2018/8/20 17:27 */ @Controller @RequestMapping("/common") public class CommonController extends RootController { @RequestMapping("/result") public ModelAndView result() { WeuiResultPage view = new WeuiResultPage(); view.setIcon("success"); view.setTitle("授权成功"); view.setBtnPrimary("确定"); view.setBtnPrimaryAction("wx.closeWindow();"); //使用微信JS-SDK提供的API关闭当前的网页窗口 view.setBtnDefault("取消"); view.setBtnDefaultAction("wx.closeWindow();"); return view.modelAndView(); } }[/mdx_fold]
View
layout:decorate布局的基本方式:先写一个布局页。
然后在每一个具体页面中通过layout:decorate指定使用的布局页(按照我的理解就是继承),可以选择性重写布局页中的某些fragment(片段)。
[mdx_fold title="布局页 layout.html"]<!DOCTYPE html> <html lang="zh-cmn-Hans" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"> <!-- 上面xmlns:th、xmlns:layout很重要,分别为下面的th:href, layout:fragment等标签元素指定所属的命名空间 --> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0,viewport-fit=cover"> <title>Layout</title> <!-- 引入Css、js、image等静态资源时,路径是相对于resources/static,而且前面要带上/ --> <link rel="stylesheet" th:href="@{/style/weui.min.css}"/> <!--<link rel="stylesheet" href="./example.css"/>--> </head> <body ontouchstart> <div class="weui-toptips weui-toptips_warn js_tooltips">错误提示</div> <div class="container" id="container"> <!-- 定义一个可以在子页面重写的fragment --> <div layout:fragment="content"></div> </div> <script th:src="@{/js/zepto.min.js}"></script> <script type="text/javascript" src="https://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script> <script src="https://res.wx.qq.com/open/libs/weuijs/1.0.0/weui.min.js"></script> <!--<script src="js/example.js"></script>--> <script> // 下面是配置微信的JS-SDk,方便在微信里面使用一个特定的Api wx.config({ debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId: '', // 必填,公众号的唯一标识 timestamp: '', // 必填,生成签名的时间戳 nonceStr: '', // 必填,生成签名的随机串 signature: '',// 必填,签名,见附录1 jsApiList: ['closeWindow'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2 }); </script> </body> </html>[/mdx_fold]
下面是一个具体的子页面:值得注意的是第四行的layout:decorate的路径写法,和静态资源的写法类似,不过这里是相对于resources/templates, 在thymeleaf3中推荐使用~{}来选择布局页面。
[mdx_fold title="结果页 result_fragment"]<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{/weui/layout}"> <head> <title th:text="${title}">结果页</title> <!--每页单独引入的文件在这写↓--> <!--<script th:src="@{../js/alert.js}"></script>--> </head> <body> <div layout:fragment="content"> <!--在这里写入页面内容↓--> <div id="content"> <div class="page"> <div class="weui-msg"> <div class="weui-msg__icon-area"><i th:class="'weui-icon_msg weui-icon-'+${icon}" ></i></div> <div class="weui-msg__text-area"> <h2 class="weui-msg__title" th:text="${title}">操作成功</h2> <p class="weui-msg__desc" th:text="${content}"></p> </div> <div class="weui-msg__opr-area"> <p class="weui-btn-area"> <a th:href="'javascript:'+${btnPrimaryAction}" class="weui-btn weui-btn_primary" th:text="${btnPrimary}">确定</a> <a th:href="'javascript:'+${btnDefaultAction}" class="weui-btn weui-btn_default" th:text="${btnDefault}">取消</a> </p> </div> <div class="weui-msg__extra-area"> <div class="weui-footer"> <p class="weui-footer__links"> <a href="javascript:void(0);" class="weui-footer__link">ICHARM</a> </p> <p class="weui-footer__text">Copyright © 2018 icharm.me</p> </div> </div> </div> </div> </div> </div> </body> </html>[/mdx_fold]
页面效果图:
Comments | NOTHING