Spring Boot中Thymeleaf和htmx助手工具库


该项目提供注释、辅助类和Thymeleaf方言,以便 在Spring Boot应用程序中轻松使用htmx。

  • htmx-spring-boot:提供注释和帮助器类。

<dependency>
    <groupId>io.github.wimdeblauwe</groupId>
    <artifactId>htmx-spring-boot</artifactId>
    <version>LATEST_VERSION_HERE</version>
</dependency>

  • htmx-spring-boot-thymeleaf:提供Thymeleaf方言以轻松使用 htmx 属性。

<dependency>
    <groupId>io.github.wimdeblauwe</groupId>
    <artifactId>htmx-spring-boot-thymeleaf</artifactId>
    <version>LATEST_VERSION_HERE</version>
</dependency>

包含的 Spring Boot 自动配置将启用 htmx 集成。

1、将控制器方法映射到 htmx 请求
控制器方法可以注释为 HxRequest,只有在基于 htmx 的请求(如 hx-get)时才会被选中。

只有当请求是由 htmx 发出时,才会调用以下方法。

@HxRequest
@GetMapping("/users")
public String htmxRequest(){
    return
"partial";
}

此外,如果要将控制器方法的调用限制在特定触发元素上,可以将 HxRequestvalue 设置为元素的 ID 或名称。如果想更明确,可使用 HxRequesttriggerId 或 HxRequesttriggerName

@HxRequest("my-element")
@GetMapping(
"/users")
public String htmxRequest(){
    return
"partial";
}

如果您想将控制器方法的调用限制为已定义的特定目标元素,请使用 HxRequesttarget

@HxRequest(target = "my-target")
@GetMapping(
"/users")
public String htmxRequest(){
    return
"partial";
}

使用 HtmxRequest 访问 htmx 发送的 HTTP 请求头
HtmxRequest 对象可用作控制器方法参数,以访问各种 htmx 请求头。

@HxRequest
@GetMapping("/users")
public String htmxRequest(HtmxRequest htmxRequest) {
    if(htmxRequest.isHistoryRestoreRequest()){
       
// do something
    }
    return
"partial";
}

响应标头
有两种方法可以在控制器方法上设置 htmx 响应头。

  • 第一种是使用注解,例如 @HxTrigger,
  • 第二种是使用 HtmxResponse 类作为控制器方法的返回类型。

目前支持以下注解:

示例
如果希望 htmx 在处理完响应后触发一个事件,可以使用注解 @HxTrigger 来设置必要的响应头 HX-Trigger。

@HxRequest
@HxTrigger("userUpdated") // htmx 将触发 "userUpdated "事件
@GetMapping(
"/users")
public String hxUpdateUser(){
    return
"partial";
}

如果想以更灵活的方式实现相同的功能,可以在控制器方法中使用 HtmxResponse 作为返回类型。

@HxRequest
@GetMapping("/users")
public HtmxResponse hxUpdateUser(){
    return HtmxResponse.builder()
        .trigger(
"userUpdated") // the event 'userUpdated' will be triggered by htmx
        .view(
"partial")
        .build();
}

Out Of Band Swaps
htmx 支持通过在单个响应中返回多个部分来更新多个目标,这就是所谓的带外交换 Out Of Band Swaps
为此,请使用 HtmxResponse 作为控制器方法的返回类型,并在其中添加多个模板。

@HxRequest
@GetMapping("/partials/main-and-partial")
public HtmxResponse getMainAndPartial(Model model){
    model.addAttribute(
"userCount", 5);
    return HtmxResponse.builder()
        .view(
"users-list")
        .view(
"users-count")
        .build();
}

HtmxResponse 可以由视图名称(如上所述)或完全解析的 View 实例(如果控制器知道如何进行解析)或 ModelAndView 实例(已解析或未解析)组成。例如

@HxRequest
@GetMapping("/partials/main-and-partial")
public HtmxResponse getMainAndPartial(Model model){
    return HtmxResponse.builder()
        .view(new ModelAndView(
"users-list")
        .view(new ModelAndView(
"users-count", Map.of("userCount",5))
        .build();
}

使用 ModelAndView 意味着每个片段都可以有自己的模型(在渲染前与控制器模型合并)。

错误处理程序
可以使用 HtmxResponse 作为错误处理程序的返回类型。这样就可以很容易地声明一个全局错误处理程序,只要出现错误,它就会在某处显示一条信息:

@ExceptionHandler(Exception.class)
public HtmxResponse handleError(Exception ex) {
    return HtmxResponse.builder()
                       .reswap(HtmxReswap.none())
                       .view(new ModelAndView("fragments :: error-message", Map.of("message", ex.getMessage())))
                       .build();
}

这将覆盖任何有异常的 htmx 请求的正常交换行为,以避免发生交换。如果错误信息片段被声明为 "带外交换",而页面布局中又有一个空 div 来 "接收 "该 HTML 片段,那么屏幕上将只显示该片段。

Spring 安全性
库中有一个 HxRefreshHeaderAuthenticationEntryPoint,可以用来让 htmx 在身份验证失败时强制刷新整个页面。如果不使用该功能,登录页面可能会出现在你要进行的交换的地方。详细信息请参阅 htmx-authentication-error-handling 博文。

要使用它,请像这样将其添加到安全配置中:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http)throws Exception{
    // 这里可能还有其他配置

    var entryPoint = new HxRefreshHeaderAuthenticationEntryPoint();
    var requestMatcher = new RequestHeaderRequestMatcher(
"HX-Request");
    http.exceptionHandling(exception ->
        exception.defaultAuthenticationEntryPointFor(entryPoint, requestMatcher));
    return http.build();
}

Thymeleaf

1、标记选择器和带外交换
用于 Spring 的 Thymeleaf 集成支持为视图指定标记选择器。标记选择器将用于选择模板中需要处理的部分,而舍弃模板的其他部分。这在处理 htmx 和带外交换(Out Of Band Swaps)等只需返回部分模板的情况时非常方便。

下面的示例通过 HtmxResponse 将两个部分与一个标记选择器相结合,该标记选择器从模板用户中选择片段列表(th:fragment="list")和另一个片段计数(th:fragment="count")。

@HxRequest
@GetMapping("/partials/main-and-partial")
public HtmxResponse getMainAndPartial(Model model){
    model.addAttribute(
"userCount", 5);
    return HtmxResponse.builder()
        .view(
"users :: list")
        .view(
"users :: count")
        .build();
}

2、方言
Thymeleaf 方言拥有适当的处理器,使 Thymeleaf 能够在 htmx 相关属性中执行计算和表达。

注意 以冒号代替典型的连字符。

  • hx:get:这是一个启用了 Thymeleaf 处理功能的属性
  • hx-get:如果不需要 Thymeleaf 处理,这只是一个静态属性。

例如,这个 Thymeleaf 模板:
<div hx:get="@{/users/{id}(id=${userId})}" hx-target="otherElement">Load user details</div>

将渲染输出为:
<div hx-get="/users/123" hx-target="otherElement">Load user details</div>

Thymeleaf 方言为大多数 hx-* 属性提供了相应的处理器。

  • 小心在值中使用 #。如果你使用 hx:target="mydiv",那么这将不起作用,因为 Thymeleaf 对翻译键使用 # 符号。要么使用 hx-target="mydiv" 或 hx:target="${'mydiv'}"

映射支持 hx:vals
hx-vals 属性允许添加到将随 AJAX 请求提交的参数中。该属性的值应为 JSON 字符串。

通过添加对内联map的支持,该库使编写这样的 JSON 字符串变得更加容易。

例如,这个 Thymeleaf 表达式:

<div hx:vals="${ {id: user.id } }"></div>

将渲染输出为:
<div hx-vals="{&amp;quot;id&amp;quot;: 1234 }"></div>

(鉴于 user.id 的值为 1234)