需求背景
通常我們會(huì)碰到一些帶表格的報(bào)表需求,一般是包含一些表格和一些循環(huán)列表的需求切蟋,每個(gè)表格的樣式可以不一樣融柬。
有些公司會(huì)使用一些專業(yè)的報(bào)表軟件鸽素,例如jasperReport隆判,如意報(bào)表等,雖然這種軟件功能很強(qiáng)大俯渤,但是需要一定的學(xué)習(xí)門檻呆细,而且使用中調(diào)整樣式并沒有想象中方便,最重要的是此類模板設(shè)計(jì)八匠、模板導(dǎo)出絮爷、模板上傳都是跟業(yè)務(wù)系統(tǒng)分開的,不能嵌入系統(tǒng)中使用梨树,不能做到在線編輯坑夯,在線預(yù)覽。
為此抡四,我專門研究了thymeleaf來完成此類需求的辦法柜蜈,希望做到報(bào)表上的所有表格都可以通過配置配出來,支持純文本和動(dòng)態(tài)變量指巡,支持圖片淑履,所有表格的樣式可以通過css來隨意配置,支持循環(huán)列表藻雪,支持橫向和縱向合并單元格
為什么選擇thymeleaf而不是選擇freemark等其他模板技術(shù)秘噪,主要基于兩個(gè)原因:
1、spring官方推薦用thymeleaf勉耀,并且springboot有官方適配
2指煎、thymeleaf編寫的模板能直接打開預(yù)覽,不會(huì)像freemark破壞了html原本的格式便斥。
實(shí)現(xiàn)思路
核心思路是:設(shè)計(jì)一個(gè)根模板(root-template.html)至壤,它可以通過設(shè)計(jì)好的數(shù)據(jù)庫配置生成新的具體模板(biz-template.html),并且biz-template.html里面包含具體的業(yè)務(wù)數(shù)據(jù)的變量椭住,這樣就可以結(jié)合業(yè)務(wù)數(shù)據(jù)展示出報(bào)表崇渗。這就是我稱之為雙層模板的原因。
技術(shù)細(xì)節(jié)包含以下幾點(diǎn):
1京郑、根模板(root-template.html)如何設(shè)計(jì)
2、需要用thymeleaf解析html并生成html葫掉,就必須使用API的方式去實(shí)現(xiàn)些举,且html的路徑可配置,否則線上環(huán)境無法生成html到j(luò)ar包里
3俭厚、生成后的html也需要包含thymeleaf的標(biāo)簽户魏,那就需要研究thymeleaf如何生成帶標(biāo)簽的模板
4、樣式和合并單元格如何控制
根模板設(shè)計(jì)
根頁面定好10個(gè)table區(qū)域,每個(gè)區(qū)域都是一樣的邏輯叼丑,判斷table配置的數(shù)據(jù)集是否為空关翎,為空則整個(gè)table不顯示,不為空鸠信,則先循環(huán)table配置的tr信息纵寝,tr里面再循環(huán)配置的td信息。
根模板如下:
springboot整合thymeleaf星立,并支持api的方式生成html
1爽茴、pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
</dependencies>
2、application.properties
server.port=8080
spring.thymeleaf.cache=false
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.check-template-location=true
spring.thymeleaf.suffix=.html
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.content-type=text/html
spring.thymeleaf.mode=HTML5
report.templates.path=
3绰垂、ThymeleafConfig配置類
@Configuration
@EnableWebMvc
public class ThymeleafConfig extends WebMvcAutoConfiguration {
@Autowired
ApplicationContext applicationContext;
/**
* eg: E:/data/resources/templates/
*/
@Value("${report.templates.path}")
private String reportTemplatesPath;
@Bean
public ViewResolver viewResolver(SpringTemplateEngine templateEngine) {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine);
viewResolver.setCache(false);
return viewResolver;
}
@Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine engine = new SpringTemplateEngine();
// engine.setEnableSpringELCompiler(true);
engine.setTemplateResolver(templateResolver());
return engine;
}
@Bean
public ITemplateResolver templateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
URL resource = this.getClass().getClassLoader().getResource("templates/"); //這里把系統(tǒng)獲取到的Class的path替換為源碼對應(yīng)的Path室奏,這樣修改的時(shí)候就可以動(dòng)態(tài)刷新
String devResource = resource.getFile().toString().replaceAll("target/classes", "src/main/resources");
if (reportTemplatesPath!=null&&!"".equals(reportTemplatesPath.trim())){
devResource=reportTemplatesPath.trim();
}
resolver.setPrefix("file://"+devResource);
resolver.setCacheable(false);//不允許緩存
resolver.setSuffix(".html");
return resolver;
}
}
這樣就可以使用以下代碼來生成html文件了
@Autowired
private TemplateEngine templateEngine;
Context context = new Context();
context.setVariable("table1", templateData.getTable1());
FileWriter write = new FileWriter(devResource+"/biz-template.html");
templateEngine.process("root-template", context, write);
利用thymeleaf生成帶thymeleaf標(biāo)簽的html文件
主要利用兩點(diǎn),
-
一個(gè)是th:attr
例如劲装,根模板里面:
<div th:attr="'th:text'=${td.value}"></div>
如果td.value=${userName}胧沫,則通過根模板生成后的子模板html代碼是:
<div th:text="${userName}"></div>
看到?jīng)],子模板就也可以再用thymeleaf進(jìn)行解析
這里有兩點(diǎn)特別說明:
1占业、<div th:attr="'th:attr'=${td.value}"></div> 這種方式行不通绒怨,通過th:attr無法增加th:attr標(biāo)簽
2、<div th:attr="'th:text'=${td.value}" th:text="${td.value}"></div> 這種方式也不行纺酸,只能解析后面那個(gè)th:text窖逗,解析結(jié)果如下:
<div>${userName}</div>
那如果又想加th:text標(biāo)簽又想設(shè)置text內(nèi)容怎么辦呢?答案是增加一層div:
<div th:attr="'th:text'=${td.value}" ><div th:text="${td.value}"></div></div>
-
一個(gè)是th:utext
例如餐蔬,根模板里面:
<div th:utext="${td.value}"></div>
如果td.value是以下字符串<img th:src="${imgUrl}">碎紊,則通過根模板生成后的子模板html代碼是:
<div><img th:src="${imgUrl}"></div>
不錯(cuò)吧,這樣也可以再用thymeleaf解析
樣式和合并單元格問題
使用th:style,th:class,th:rowspan,th:clospan來控制
根模板中要設(shè)置全局自定義樣式變量可以使用如下配置:
<style th:inline="text">
[[${style}]]
</style>
效果演示
子模板template2
子模板2填充數(shù)據(jù)后:
子模板template3
子模板3填充數(shù)據(jù)后:
最終的html如果需要轉(zhuǎn)成pdf樊诺,請參考最好的html轉(zhuǎn)pdf工具(wkhtmltopdf)
常用語法
注釋
1仗考、靜態(tài)文件打開可以看到,但是thymeleaf生成后看不到
<!--/*-->
<div>
you can see me only before Thymeleaf processes me!
</div>
<!--*/-->
2词爬、靜態(tài)文件打開看不到秃嗜,但是thymeleaf生成后看得到
<span>hello!</span>
<!--/*/
<div th:text="${...}">
...
</div>
/*/-->
<span>goodbye!</span>
3、塊代碼段顿膨,th:block锅锨,可以配合thymeleaf的其他標(biāo)簽去定義代碼塊,例如th:each如果循環(huán)單行<tr>的時(shí)候恋沃,th:each可以放在<tr>標(biāo)簽里必搞,但是如果要循環(huán)多行<tr>的話,則可以配合th:block來使用囊咏,如下:
<table>
<th:block th:each="user : ${users}">
<tr>
<td th:text="${user.login}">...</td>
<td th:text="${user.name}">...</td>
</tr>
<tr>
<td colspan="2" th:text="${user.address}">...</td>
</tr>
</th:block>
</table>
刪除代碼段
以下是Thymeleaf的一個(gè)例子恕洲。我們可以使用th:remove來刪除指定的部分塔橡,這在原型設(shè)計(jì)和調(diào)試的時(shí)候很有用。
<tr th:remove="all">
<td>Mild Cinnamon</td>
<td>1.99</td>
<td>yes</td>
<td>
<span>3</span> comment/s
<a href="comments.html">view</a>
</td>
</tr>
th:remove可接受的值有5個(gè):
- all: 移除標(biāo)簽和所有子元素
- body: 移除所有子元素霜第,保留標(biāo)簽
- tag: 移除標(biāo)簽葛家,保留子元素
- all-but-first: 保留第一個(gè)子元素,移除所有其他
- none : 什么也不做泌类。這個(gè)值在動(dòng)態(tài)求值的時(shí)候會(huì)有作用
特別注意點(diǎn)
Thymeleaf中無法利用map.key表達(dá)式獲取到map的鍵值癞谒,特別是使用 th:if="map.key =null" 的時(shí)候,如果map里面不包含key末誓,則立馬報(bào)錯(cuò)扯俱,所以如果要用從map里面取某個(gè)key,則map里面必須要有這個(gè)key值(value為空也必須設(shè)置為null)喇澡,否則會(huì)報(bào)錯(cuò)
參考
Springboot 之 引入Thymeleaf
thymeleaf 基本語法
spring boot(四):thymeleaf使用詳解
Spring Web MVC框架(十二) 使用Thymeleaf
使用Thymeleaf API渲染模板生成靜態(tài)頁面
Spring Boot中Thymeleaf引擎動(dòng)態(tài)刷新
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html