- Ajax是一种在不重新加载整个页面的情况下,能够与服务器交换数据并更新部分网页的技术。
- HTMX可能成为将Java后端与前端动态交互功能紧密结合的工具。
让我们通过基于 HTMX、Spring Boot 和 Thymeleaf 的示例应用程序来一探究竟。
什么是HTMX?
HTMX是一种较新的技术,它采用普通的 HTML,并赋予其 Ajax 和 DOM 交换等额外功能。它被列入我个人的好主意列表中,因为它消除了典型 Web 应用程序中的整个复杂性。HTMX 通过在 JSON 和 HTML 之间来回转换来工作。可以将其视为一种声明式 Ajax。
Java、Spring 和 Thymeleaf
而 Java 则是另一个选择:它是最成熟且最具创新性的服务器端平台之一。Spring 是添加一系列基于 Java 的功能的简单选择,包括用于处理端点和路由的 精心设计的Spring Boot Web 项目。
Thymeleaf是一款完整的服务器端模板引擎,也是 Spring Boot Web 的默认引擎。与 HTMX 结合使用时,您可以构建全栈 Web 应用,而无需使用大量 JavaScript。
案例
我们将构建标准的 Todo 应用:我们列出现有的待办事项,并允许创建新的待办事项、删除待办事项以及更改其完成状态。
概述
完成的 Todo 应用程序在磁盘上的样子如下:
$ tree |
因此,除了典型的 Gradle 内容外,应用程序还有两个主要部分包含在 /src 目录中:/main 目录包含 Java 代码,而 /resources 则包含属性文件以及 CSS 和 Thymeleaf 模板的两个子目录。
您可以在 GitHub repo 代码库中找到该项目的源代码。要运行它,请访问根目录并键入 $ gradle bootRun。然后就可以在 localhost:8080 上使用该应用程序了。
如果想从头开始启动应用程序,可以从以下步骤开始:$ spring init --dependencies=web,thymeleaf spring-htmx。这将把 Thymeleaf 和 Spring Boot 安装到 Gradle 项目中。
该应用程序是由 DemoApplication.java 运行的普通 Spring Boot 应用程序。
Java Spring HTMX 模型类
让我们首先看看我们的模型类:com/example/iwjavaspringhtmx/TodoItem.java。这是代表待办事项的服务器端模型类。它看起来如下:
public class TodoItem { |
这是一个带有 getter 和 setter 的简单模型类。没什么特别的,但这正是我们想要的。
Java Spring HTMX 控制器类
在服务器上,控制器是老板。它接受请求、编排逻辑并制定响应。在我们的例子中,我们需要四个端点,用于列出项目、更改其完成状态、添加项目和删除项目。这是控制器类:
@Controller |
您会注意到,我刚刚创建了一个静态变量List来保存内存中的项目。在现实生活中,我们会使用外部数据存储。
首先,端点使用 @GetMapping、@PostMapping 和 @DeleteMapping 进行注解。这就是将 Spring Web 路径映射到处理程序的方法。每个注解对应其 HTTP 方法(GET、POST、DELETE)。
Spring Boot 还可以使用参数注解 @PathParameter 从路径中轻松获取参数。因此,对于 /todos/{id}/delete 路径,@PathVariable Integer id 将包含路径 {id} 部分的值。
在 createTodo() 方法中,注释为 @ModelAttribute TodoItem newTodo 的参数将自动接收 POST 主体并将其值应用于 newTodo 对象。(这是一种将表单提交转化为 Java 对象的快速而简单的方法)。
接下来,我们使用项目 ID 来操作项目列表:这是标准的 REST API 方法。
发送响应有两种方式。如果方法上有 @ResponseBody 注解(比如 deleteTodo()),那么返回的内容将逐字发送。否则,返回字符串将被解释为 Thymeleaf 模板路径(稍后您将看到)。
Model 模型参数比较特殊。它用于为移交给 Thymeleaf 的作用域添加属性。我们可以把下面的项目方法理解为给定一个指向根/路径的 GET 请求,将 items 变量添加到作用域中,命名为 "itemList",然后使用 "index "模板渲染响应。
@GetMapping("/") |
在 HTMX 处理从前端发送的 AJAX 请求时,HTMX 组件将使用响应来更新用户界面。我们很快就能在实践中很好地了解这一点。
Thymeleaf 模板
现在让我们来看看 Thymeleaf 的索引模板。它位于 /resources/templates/index.html 文件中。Spring Boot 使用约定将 items() 方法返回的 "index "字符串映射到该文件。下面是我们的 index.html 模板:
<!DOCTYPE html> |
Thymeleaf 的基本思想是在 HTML 结构中使用 Java 变量。(这相当于使用 Pug 这样的模板系统)。
Thymeleaf 使用以 th: 为前缀的 HTML 属性或元素来表示其工作位置。请记住,当我们在控制器中映射根/路径时,我们在作用域中添加了 itemList 变量。在这里,我们在带有 th:each 属性的 th:block 中使用该变量。th:each 属性是 Thymeleaf 中的迭代器机制。我们用它来访问 itemList 中的元素,并将每个元素作为名为 item 的变量公开:item : ${itemList}。
在 itemList 的每次迭代中,我们都将渲染工作交给另一个模板。这种模板重用是避免代码重复的关键。
行:
<th:block th:replace="~{'todo.html'}"th:args="${item}"></th:block> |
告诉 Thymeleaf 渲染 todo.html 模板,并将项目作为参数。
接下来我们将了解 todo 模板,但首先要注意的是,我们在控制器中的 completeTodo 和 createTodo 中都使用了相同的模板,以提供在 Ajax 请求期间发送回 HTMX 的标记。换句话说,我们将 todo.html 用作初始列表渲染的一部分,并在 Ajax 请求期间向用户界面发送更新。重复使用 Thymeleaf 模板可以使我们保持 DRY。
待办事项模板
这是 todo.html:
<li> |
您可以看到,我们提供了一个 list-item 元素,并使用一个变量 item 来填充值。在这里,我们将使用 HTMX 和 Thymeleaf 进行一些有趣的工作。
首先,我们使用 th:checked 将 item.isComplete 的选中状态应用于复选框输入。
点击复选框时,我们会使用 HTMX 向后端发出 Ajax 请求:
- hx-trigger="click" 告知 HTMX 在点击时启动 Ajax。
- hx-target="closest li "告诉 HTMX 将 Ajax 请求的响应放在哪里。在我们的例子中,我们要替换最近的 list-item 元素。(请记住,我们的删除端点会返回该项目的整个列表项目标记)。
- hx-swap="outerHTML "告诉 HTMX 如何替换新内容,在本例中就是替换整个元素。
- th:hx-post="|/todos/${item.id}/complete|"告诉 HTMX,这是一个活动的 Ajax 元素,会向指定的 URL(我们的 completeTodo 端点)发出 POST 请求。
将 Thymeleaf 与 HTMX 结合使用时需要注意的一点是,您最终会使用复杂的属性前缀,就像您在 th:hx-post 中看到的那样。从本质上讲,Thymeleaf 首先在服务器上运行(th: 前缀)并填充 ${item.id} 插值,然后 hx-post 在客户端上正常工作。
接下来,对于 span,我们只需显示 item.description 中的文本。(请注意,Thymelef 的表达式语言允许我们访问字段而无需使用 get 前缀)。同样值得注意的是,我们如何在 span 元素中应用已完成的样式类。下面是我们的 CSS 将用于为已完成的项目添加删除线装饰:
th:classappend="${item.isCompleted ? 'complete' : ''}" |
使用 Thymeleaf 属性,可以根据 item.isComplete 等布尔条件有条件地应用类。
我们的删除按钮与完整复选框的工作原理类似。我们使用 Thymeleaf 提供的 item.id 向 URL 发送 Ajax 请求,当响应返回时,我们更新列表项。请记住,我们从 deleteTodo() 发回的是空字符串。因此,其效果是从 DOM 中移除列表项。
CSS 样式表
CSS 样式表位于 src/main/resources/static/style.css,没什么特别的。唯一有趣的是处理跨页上的span样式:
span { |
结论
将 HTMX、Java、Spring 和 Thymeleaf 结合在一起,可以用最少的模板代码构建相当复杂的交互。我们可以在不编写 JavaScript 的情况下实现大量典型的交互。
乍一看,Java-HTMX 协议栈似乎终于兑现了以 Java 为中心的 Ajax 的承诺;就像 Google Web Toolkit 曾经设定的目标一样。
但事实并非如此。
HTMX 试图将Web应用程序重新定位到 REST 的真正本质,而这个协议栈为我们指明了方向。
HTMX 与服务器端无关,因此我们可以毫无困难地将其与 Java 后端集成。