SpringBoo+HTMX程式設計簡介

banq發表於2024-03-16

HTMX 是一個小型 JavaScript 庫,可讓您使用 HTML 中的自定義屬性來定義頁面中元素的行為。它有點像現代版的 onclick 屬性,但功能更強大、更靈活。

它的效率也更高,因為它使用瀏覽器內建的 HTTP 協議棧來發出請求,而且可以使用瀏覽器內建的快取和歷史記錄管理功能。

它非常適合 Spring Boot 等伺服器端框架,因為它允許使用伺服器來生成頁面的內容和行為,並允許使用瀏覽器的內建功能來管理導航和歷史記錄。

HTMX安裝
1、最簡單的方法是從 CDN 抓取,然後新增到 layout.html 模板中:
<script src='https://unpkg.com/htmx.org/dist/htmx.min.js'></script>

2、使用 Webjar 將庫載入到 classpath 中,這樣也可以正常工作。Spring 可以做一些額外的事情來幫助瀏覽器快取程式庫,還可以幫助進行版本管理。見:GitHub (dsyer/webmvc-thymeleaf).


表單處理
我們可以輕鬆新增的一項功能是使用 HTMX 提交表單,而無需重新載入整個頁面。為此,我們可以在表單元素中新增 hx-post 屬性:

<form th:action=<font>"@{/greet}" method="post" hx-post="/greet">
    <input type=
"text" name="name" th:value="${name}"/>
    <button type=
"submit" class="btn btn-primary">Greet</button>
</form>

這將導致 HTMX 攔截表單上的提交操作,並使用 AJAX 請求將資料傳送到伺服器。伺服器將處理請求並返回結果,HTMX 將用結果替換表單內容。

如果不希望結果替換表單內容的效果,可以透過在表單元素中新增 hx-target 屬性來解決這個問題:

<form th:action=<font>"@{/greet}" th:hx-post="@{/greet}" method="post" hx-target="content">

 "conent"元素:包含ID 和 hx-swap-oob 屬性,以實現將傳入的內容應替換現有內容:

<div id=<font>"content" class="col-md-12" hx-swap-oob="true">
    <span th:text=
"${greeting}">Hello, World</span><br/>
    <span th:text=
"${time}">21:00</span>
</div>

這樣,HTMX 就提取 "content "元素併為我們切換其內容。圖片和其他靜態內容不會重新載入,瀏覽器的歷史記錄也會更新,以反映頁面的新狀態。

後端springboot程式碼:

@PostMapping(path = <font>"/greet")
String name(Map<String, Object> model, @RequestParam String name) {
    greet(model);
    model.put(
"greeting", "Hello " + name);
    model.put(
"name", name);
    return
"greet";
}

HTMX 在向後端伺服器/greet發出的請求中新增了 hx-request 標頭:這是 HTMX 的一項功能,可讓您在伺服器端程式碼中匹配請求,接下來我們將使用該功能。

使用片段模板
現在,伺服器仍會為表單提交重新渲染整個頁面,可以透過使用片段模板來提高效率。

在 greet.html 模板中新增 th:fragment 屬性:

<div id=<font>"content" th:fragment="content" class="col-md-12" hx-swap-oob="true">
    <span th:text=
"${greeting}">Hello, World</span><br/>
    <span th:text=
"${time}">21:00</span>
</div>

然後,我們就可以在 SampleController 中的一個新對映方法中使用該片段,該方法僅在請求來自 HTMX 時觸發(透過匹配 hx-request 頭資訊):

@PostMapping(path = <font>"/greet", headers = "hx-request=true")
String nameHtmx(Map<String, Object> model, @RequestParam String name) {
    greet(model);
    return
"greet :: content";
}

(":: "語法是 Thymeleaf 的一項功能,可讓您呈現模板的片段。例如,找到 "greet "模板,然後查詢名為 "content "的片段)。

如果現在提交表單,並檢視瀏覽器開發工具中的網路活動,就會發現伺服器只返回了更新內容所需的頁面片段。

懶載入
另一種常見用例是在頁面首次載入時從伺服器載入內容,甚至可以根據使用者的偏好進行定製。

我們可以使用 HTMX 來實現這一功能,方法是在要觸發請求的元素上新增 hx-get 屬性。

我們可以在 layout.html 模板中嘗試使用徽標,而不是靜態地包含圖片。

下面是靜態包含圖片:

<div class=<font>"row">
    <div class=
"col-12">
    <img src=
"../static/images/spring-logo.svg" th:src="@{/images/spring-logo.svg}" alt="Logo" style="width:200px;" loading="lazy">
    </div>
</div>

上述程式碼替換為:

<div class=<font>"row">
    <div class=
"col-12">
    <span class=
"fa fa-spin fa-spinner" style="width:200px; text-align:center;">
    </div>
</div>

上述程式碼使用了佔位符替換img位置

讓 HTMX 動態載入:

<div class=<font>"row">
    <div class=
"col-12" hx-get="/logo" hx-trigger="load">
    <span class=
"fa fa-spin fa-spinner" style="width:200px; text-align:center;">
    </div>
</div>

注意:這裡新增了 hx-get 和 hx-trigger。

  • hx-get 屬性告訴 HTMX 向伺服器發出 GET 請求,以獲取元素的內容。
  • hx-trigger 屬性告訴 HTMX 在頁面載入時觸發請求。預設情況下是在點選時觸發。

因此,我們需要在 SampleController 中建立一個新的對映:

@GetMapping(path = <font>"/logo")
String logo() {
    return
"layout :: logo";
}

為了只渲染 layout.html 模板中包含圖片的片段,必須再次修改 layout.html 模板,使用 th:fragment 屬性,替換hx-get和hx-trigger 屬性:

<div class=<font>"row" th:remove="all">
    <div class=
"col-12" th:fragment="logo">
    <img src=
"../static/images/spring-logo.svg" th:src="@{/images/spring-logo.svg}" alt="Logo"
        style=
"width:200px;" loading="lazy">
    </div>
</div>

請注意,我們必須從模板中th:remove該片段,因為佔位符將在初始呈現時替換它。

如果現在執行應用程式,就會看到頁面載入時,旋轉圖示就被真實後端的logo圖片取代。

Spring Boot HTMX其他庫包
HTMX 還有更多的功能,我們沒有時間在此詳細介紹。值得一提的是,有一個 Java 庫可以幫助實現這些功能,而且還有一些 Thymeleaf 工具:Wim Deblauwe 編寫的 Spring Boot HTMX ,可作為 Maven Central 的依賴項。它可以透過自定義註解進行 hx-request 頭匹配,還能幫助實現 HTMX 的其他功能。

Unpoly庫包
Unpoly的CDN連結是:

<script src='https://unpkg.com/unpoly/unpoly.min.js'></script>

示例程式碼中的“unpoly”分支和以前一樣使用 Webjar。基本(整個頁面呈現)表單提交示例如下所示:

<div class=<font>"col-md-12">
    <form th:action=
"@{/greet}" method="post" up-target="content">
    <input type=
"text" name="name" th:value="${name}"/>
    <button type=
"submit" class="btn btn-primary">Greet</button>
    </form>
</div>
<div id=
"content" class="col-md-12">
    <span th:text=
"${greeting}">Hello, World</span><br/>
    <span th:text=
"${time}">21:00</span>
</div>

hx-target變成了up-target,其餘的 HTMX 裝飾只是 Unpoly 中的預設設定。

要轉換為片段模板,我們需要遵循 HTMX 中的模式:新增一個th:fragment與 Unpoly 中的唯一標頭匹配的控制器方法,例如X-Up-Context。

Hotwired Turbo庫包
Hotwired Turbo 的 CDN 連結是:

<script src='https://unpkg.com/@hotwired/turbo/dist/turbo.es2017-umd.js'></script>
示例程式碼中的“turbo”分支和以前一樣使用 Webjar。基本表單提交示例如下所示:

<turbo-frame id=<font>"content">
    <div class=
"col-md-12">
    <form th:action=
"@{/greet}" method="post">
        <input type=
"text" name="name" th:value="${name}" />
        <button type=
"submit" class="btn btn-primary">Greet</button>
    </form>
    </div>
    <div class=
"col-md-12">
        <span th:text=
"${greeting}">Hello, World</span><br />
        <span th:text=
"${time}">21:00</span>
    </div>
</turbo-frame>

Turbo 使用自定義元素 ( turbo-frame) 來標識要替換的內容,而不是標識表單處理互動的自定義​​屬性。表格的其餘部分不變。

要轉換為片段模板,我們需要th:fragment向<turbo-frame>和控制器方法新增一個宣告,以匹配來自 Turbo 的唯一標頭,例如Turbo-Frame。

 

相關文章