暢購商城(七):Thymeleaf實現靜態頁
好好學習,天天向上
本文已收錄至我的Github倉庫DayDayUP:github.com/RobodLee/DayDayUP,歡迎Star,更多文章請前往:目錄導航
Thymeleaf簡單入門
什麼是Thymeleaf
Thymeleaf是一個模板引擎,主要用於編寫動態頁面。
SpringBoot整合Thymeleaf
SpringBoot整合Thymeleaf的方式很簡單,共分為以下幾個步驟
- 建立一個sprinboot專案
- 新增thymeleaf和spring web的起步依賴
- 在resources/templates/下編寫html(需要宣告使用thymeleaf標籤)
- 在controller層編寫相應的程式碼
啟動類,配置檔案,依賴的程式碼下一節有,這裡就不貼了。
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>SpringBoot整合Thymeleaf</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> </head> <body> <!--輸出hello資料 ${變數名} --> <p th:text="${hello}"></p> </body> </html>
@Controller
@RequestMapping("/test")
public class TestController {
@RequestMapping("/hello")
public String hello(Model model){
model.addAttribute("hello","歡迎關注微信公眾號Robod");
return "demo1";
}
}
這樣將專案啟動起來,訪問http://localhost:8080/test/hello
Thymeleaf常用標籤
- th:action 定義後臺控制器路徑
現在訪問http://localhost:8080/test/hello2,如果控制檯輸出“demo2”,頁面還跳轉到demo2的話說明是OK的。
- th:each 物件遍歷
訪問http://localhost:8080/test/hello3就可以看到結果了。
- 遍歷Map
訪問http://localhost:8080/test/hello4就可以看到輸出結果。
- 陣列輸出
訪問http://localhost:8080/test/hello5就可以看到輸出結果。
- Date輸出
訪問http://localhost:8080/test/hello6就可以看到輸出結果。
- th:if條件
訪問http://localhost:8080/test/hello7就可以看到輸出結果。
- th:fragment th:include 定義和引入模組
比如我們在footer.html中定義了一個模組:
<div th:fragment="foot">
歡迎關注微信公眾號Robod
</div>
然後在demo7中引用:
<div th:include="footer::foot"></div>
這樣訪問http://localhost:8080/test/hello7就可以看到效果了。
- |....| 字串拼接
<span th:text="|${str1}${str2}|"></span>
--------------------------------------------
@RequestMapping("/hello8")
public String hello8(Model model){
model.addAttribute("str1","字串1");
model.addAttribute("str2","字串2");
return "demo8";
}
訪問http://localhost:8080/test/hello8就可以看到輸出結果。
想要完整程式碼的小夥伴請點選下載
搜尋頁面
微服務搭建
我們建立一個搜尋頁面渲染微服務用來展示搜尋頁面,在這個微服務中,使用者進行搜尋後,調用搜索微服務拿到資料,然後使用Thymeleaf將頁面渲染出來展示給使用者。在changgou-web下建立一個名為changgou-search-web的Module用作搜尋微服務的頁面渲染工程。因為有些依賴是所有頁面渲染微服務都要用到的,所以在changgou-web中新增依賴:
<dependencies>
<!-- Thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--feign-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.cloud</groupId>-->
<!-- <artifactId>spring-cloud-starter-openfeign</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
<!--amqp-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
</dependencies>
Feign的依賴這裡我發現了一個問題,因為我不是把SearchEntity根據下圖的流程通過Feign傳遞到changgou-service-search麼。如果新增我註釋的那個依賴就會出現HttpRequestMethodNotSupportedException: Request method 'POST' not supported
異常。新增後面一個依賴就不會出現問題。我到網上查了一下,貌似是Feign的一個小Bug,就是如果在GET請求裡添加了請求體就會被轉換為POST請求。
因為我們需要使用到Feign在幾個微服務之間進行呼叫,所以在changgou-search-web新增對changgou-service-search-api的依賴。
<dependencies>
<dependency>
<groupId>com.robod</groupId>
<artifactId>changgou-service-search-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
然後在changgou-service-search-api下編寫相應的Feign介面用來呼叫changgou-service-search:
@FeignClient(name="search")
@RequestMapping("/search")
public interface SkuEsFeign {
/**
* 搜尋
* @param searchEntity
* @return
*/
@GetMapping
Result<SearchEntity> searchByKeywords(@RequestBody(required = false) SearchEntity searchEntity);
}
然後在changgou-search-web下的resource目錄下將資料提供的靜態資源匯入進去。因為主要是做後端的功能,所以前端就不寫了,直接匯入:
最後將啟動類和配置檔案寫好:
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = "com.robod.feign")
public class SearchWebApplication {
public static void main(String[] args) {
SpringApplication.run(SearchWebApplication.class,args);
}
}
server:
port: 18086
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:7001/eureka
instance:
prefer-ip-address: true
spring:
thymeleaf:
#構建URL時預先檢視名稱的字首,預設就是這個,寫在這裡是怕忘了怎麼配置
prefix: classpath:/templates/
suffix: .html #字尾
cache: false #禁止快取
feign:
hystrix:
enabled: true
application:
name: search-web
main:
allow-bean-definition-overriding: true
# 不配置下面兩個的話可能會報timed-out and no fallback available異常
ribbon:
ReadTimeout: 500000 # Feign請求讀取資料超時時間
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 50000 # feign連線超時時間
這樣我們的搜尋頁面微服務工程就搭建好了。然後在建立一個SkuSearchWebController類,然後建立一個searchByKeywords方法作為搜尋功能的入口
@GetMapping("/list")
public String searchByKeywords(SearchEntity searchEntity
, Model model) {
if (searchEntity == null || StringUtils.isEmpty(searchEntity.getKeywords())) {
searchEntity = new SearchEntity("小米");
}
if (searchEntity.getSearchSpec() == null) {
searchEntity.setSearchSpec(new HashMap<>(8));
}
SearchEntity result = skuFeign.searchByKeywords(searchEntity).getData();
model.addAttribute("result", result);
return "search";
}
這裡我指定了一個預設的關鍵詞,因為我發現如果searchEntity為null的話Feign就會報出timed-out and no fallback available
,指定預設關鍵詞就可以解決這個問題,而且也符合邏輯,淘寶上如果不在搜尋欄填入任何內容就會搜尋預設的關鍵詞。
這個時候如果去訪問http://localhost:18086/search/list是沒有圖片和css樣式的,因為現在的seearch.html中指定的相對路徑,也就是去訪問search/img/下的圖片,其實是在img/下,所以我們還需要把相對路徑改為絕對路徑。把search中的href="./改為href="/,把src="./改為src="/,這樣訪問的就是img/下的圖片了。頁面就可以正常顯示了。
這樣的話搜尋頁面渲染微服務就搭建成功了。
資料填充
現在頁面所展示的資料並不是我們從ES中搜索出來的真實資料,而是預先設定好的資料。所以現在我們需要把搜尋出來的資料填充到介面上。
頁面所展示的就是一堆的li
標籤,我們所需要做的就是留一個li,然後使用Themeleaf標籤迴圈取出資料填入進去。
<div class="goods-list">
<ul class="yui3-g">
<li th:each="item:${result.rows}" class="yui3-u-1-5">
<div class="list-wrap">
<div class="p-img">
<a href="item.html" target="_blank"><img th:src="${item.getImage()}"/></a>
</div>
<div class="price">
<strong>
<em>¥</em>
<i th:text="${item.price}"></i>
</strong>
</div>
<div class="attr">
<!--th:utext可以識別標籤 strings.abbreviate控制長度-->
<a target="_blank" href="item.html" title=""
th:utext="${#strings.abbreviate(item.name,150)}"></a>
</div>
<div class="commit">
<i class="command">已有<span>2000</span>人評價</i>
</div>
<div class="operate">
<a href="success-cart.html" target="_blank" class="sui-btn btn-bordered btn-danger">
加入購物車</a>
<a href="javascript:void(0);" class="sui-btn btn-bordered">收藏</a>
</div>
</div>
</li>
</ul>
</div>
頁面關鍵詞搜尋和回顯顯示
首先指定表單提交的路徑,然後指定name的值,將搜尋按鈕的type指定為“submit”就可以實現頁面關鍵詞搜尋;然後新增th:value="${result.keywords}"表示取出result.keywords的值,從而實現回顯顯示的功能。
搜尋條件回顯及條件過濾顯示
分類和品牌
如果沒有指定分類和品牌資訊的話,後端會將分類和品牌進行統計然後傳到前端,當我們指定了分類和品牌之後就不用將分類和品牌進行分類統計了,這個在上一篇文章中說過,但是前端怎麼處理呢?使用th:each遍歷出資料顯示出來,當我們指定了分類或者品牌之後,頁面上就不去顯示分類或品牌選項。
th:unless 的意思是不滿足條件才輸出資料,所以判斷一下categotyList和brandList是不是空的,是空的就不輸出內容。不是空的就用th:each遍歷,然後用th:text輸出。
規格
規格顯示和過濾和上面的類似。
searchSpec是傳到後端的規格Map<String,String>集合,sepcMap是後端傳到前端的規格Map<String,Set
但是前端怎麼給後端的searchEntity.searchSpec賦值呢?我不知道,問了一下我哥,他說這樣寫:http://www.test.com/path?map[a]=1&map[b]=2
,然後就報400錯誤了,控制檯顯示