Skip to content

Commit

Permalink
Add dedicated annotations for HX-Trigger-After-Settle and HX-Trigger-…
Browse files Browse the repository at this point in the history
…After-Swap and support multiple events
  • Loading branch information
xhaggi committed May 2, 2024
1 parent 9e8f235 commit 512d937
Show file tree
Hide file tree
Showing 8 changed files with 183 additions and 4 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ The following annotations are currently supported:
* [@HxReswap](https://javadoc.io/doc/io.github.wimdeblauwe/htmx-spring-boot/latest/io/github/wimdeblauwe/htmx/spring/boot/mvc/HxReswap.html)
* [@HxRetarget](https://javadoc.io/doc/io.github.wimdeblauwe/htmx-spring-boot/latest/io/github/wimdeblauwe/htmx/spring/boot/mvc/HxRetarget.html)
* [@HxTrigger](https://javadoc.io/doc/io.github.wimdeblauwe/htmx-spring-boot/latest/io/github/wimdeblauwe/htmx/spring/boot/mvc/HxTrigger.html)
* [@HxTriggerAfterSettle](https://javadoc.io/doc/io.github.wimdeblauwe/htmx-spring-boot/latest/io/github/wimdeblauwe/htmx/spring/boot/mvc/HxTriggerAfterSettle.html)
* [@HxTriggerAfterSwap](https://javadoc.io/doc/io.github.wimdeblauwe/htmx-spring-boot/latest/io/github/wimdeblauwe/htmx/spring/boot/mvc/HxTriggerAfterSwap.html)

>**Note** Please refer to the related Javadoc to learn more about the available options.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public HtmxHandlerInterceptor(ObjectMapper objectMapper) {
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {

if (handler instanceof HandlerMethod) {
Method method = ((HandlerMethod) handler).getMethod();
setHxLocation(response, method);
Expand All @@ -38,6 +39,8 @@ public boolean preHandle(HttpServletRequest request,
setHxRetarget(response, method);
setHxReselect(response, method);
setHxTrigger(response, method);
setHxTriggerAfterSettle(response, method);
setHxTriggerAfterSwap(response, method);
setHxRefresh(response, method);
setVary(request, response);
}
Expand Down Expand Up @@ -108,7 +111,21 @@ private void setHxReselect(HttpServletResponse response, Method method) {
private void setHxTrigger(HttpServletResponse response, Method method) {
HxTrigger methodAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, HxTrigger.class);
if (methodAnnotation != null) {
response.setHeader(getHeaderName(methodAnnotation.lifecycle()), methodAnnotation.value());
setHeaderValues(response, getHeaderName(methodAnnotation.lifecycle()), methodAnnotation.value());
}
}

private void setHxTriggerAfterSettle(HttpServletResponse response, Method method) {
HxTriggerAfterSettle methodAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, HxTriggerAfterSettle.class);
if (methodAnnotation != null) {
setHeaderValues(response, HtmxResponseHeader.HX_TRIGGER_AFTER_SETTLE.getValue(), methodAnnotation.value());
}
}

private void setHxTriggerAfterSwap(HttpServletResponse response, Method method) {
HxTriggerAfterSwap methodAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, HxTriggerAfterSwap.class);
if (methodAnnotation != null) {
setHeaderValues(response, HtmxResponseHeader.HX_TRIGGER_AFTER_SWAP.getValue(), methodAnnotation.value());
}
}

Expand Down Expand Up @@ -140,6 +157,10 @@ private void setHeaderJsonValue(HttpServletResponse response, String name, Objec
}
}

private void setHeaderValues(HttpServletResponse response, String name, String[] values) {
response.setHeader(name, String.join(",", values));
}

private HtmxLocation convertToLocation(HxLocation annotation) {
var location = new HtmxLocation();
location.setPath(annotation.path());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,24 @@
import java.lang.annotation.Target;

/**
* Annotation to trigger client side actions on the target element within a response to htmx.
* Annotation to trigger client side events as soon as the response is received on the target element.
* <br>
* You can trigger a single event or as many uniquely named events as you would like.
*
* @see <a href="https://htmx.org/headers/hx-trigger/">HX-Trigger Response Headers</a>
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface HxTrigger {
String value();

/**
* The events to trigger as soon as the response is received on the target element.
*/
String[] value();

/**
* @deprecated use annotation {@link HxTriggerAfterSettle} or {@link HxTriggerAfterSwap} instead.
*/
@Deprecated
HxTriggerLifecycle lifecycle() default HxTriggerLifecycle.RECEIVE;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.github.wimdeblauwe.htmx.spring.boot.mvc;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Annotation to trigger client side events after the
* <a href="https://htmx.org/docs/#request-operations">settling step</a>
* on the target element.
* <br>
* You can trigger a single event or as many uniquely named events as you would like.
*
* @see <a href="https://htmx.org/headers/hx-trigger/">HX-Trigger Response Headers</a>
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface HxTriggerAfterSettle {

/**
* The events to trigger after the
* <a href="https://htmx.org/docs/#request-operations">settling step</a>
* on the target element.
*/
String[] value();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.github.wimdeblauwe.htmx.spring.boot.mvc;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Annotation to trigger client side events after the
* <a href="https://htmx.org/docs/#request-operations">swap step</a>
* on the target element.
* <br>
* You can trigger a single event or as many uniquely named events as you would like.
*
* @see <a href="https://htmx.org/headers/hx-trigger/">HX-Trigger Response Headers</a>
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface HxTriggerAfterSwap {

/**
* The events to trigger after the
* <a href="https://htmx.org/docs/#request-operations">swap step</a>
* on the target element.
*/
String[] value();

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
* Represents the HX-Trigger Response Headers.
*
* @see <a href="https://htmx.org/headers/hx-trigger/">HX-Trigger Response Headers</a>
* @deprecated use annotation {@link HxTriggerAfterSettle} or {@link HxTriggerAfterSwap} instead.
*/
@Deprecated
public enum HxTriggerLifecycle {
/**
* Trigger events as soon as the response is received.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ public void testHeaderIsSetOnResponseIfHxTriggerIsPresent() throws Exception {
.andExpect(header().string("HX-Trigger", "eventTriggered"));
}

@Test
public void testHeaderIsSetOnResponseWithMultipleEventsIfHxTriggerIsPresent() throws Exception {
mockMvc.perform(get("/with-trigger-multiple-events"))
.andExpect(status().isOk())
.andExpect(header().string("HX-Trigger", "event1,event2"));
}

@Test
public void testAfterSettleHeaderIsSetOnResponseIfHxTriggerIsPresent() throws Exception {
mockMvc.perform(get("/with-trigger-settle"))
Expand All @@ -40,6 +47,43 @@ public void testAfterSwapHeaderIsSetOnResponseIfHxTriggerIsPresent() throws Exce
.andExpect(header().string("HX-Trigger-After-Swap", "eventTriggered"));
}

@Test
public void testAfterSettleHeaderIsSetOnResponseIfHxTriggerAfterSettleIsPresent() throws Exception {
mockMvc.perform(get("/with-trigger-after-settle"))
.andExpect(status().isOk())
.andExpect(header().string("HX-Trigger-After-Settle", "eventTriggered"));
}

@Test
public void testAfterSettleHeaderIsSetOnResponseWithMultipleEventsIfHxTriggerAfterSettleIsPresent() throws Exception {
mockMvc.perform(get("/with-trigger-after-settle-multiple-events"))
.andExpect(status().isOk())
.andExpect(header().string("HX-Trigger-After-Settle", "event1,event2"));
}

@Test
public void testAfterSwapHeaderIsSetOnResponseIfHxTriggerAfterSwapIsPresent() throws Exception {
mockMvc.perform(get("/with-trigger-after-swap"))
.andExpect(status().isOk())
.andExpect(header().string("HX-Trigger-After-Swap", "eventTriggered"));
}

@Test
public void testAfterSwapHeaderIsSetOnResponseWithMultipleEventsIfHxTriggerAfterSwapIsPresent() throws Exception {
mockMvc.perform(get("/with-trigger-after-swap-multiple-events"))
.andExpect(status().isOk())
.andExpect(header().string("HX-Trigger-After-Swap", "event1,event2"));
}

@Test
public void testHeadersAreSetOnResponseIfHxTriggersArePresent() throws Exception {
mockMvc.perform(get("/with-triggers"))
.andExpect(status().isOk())
.andExpect(header().string("HX-Trigger", "event1,event2"))
.andExpect(header().string("HX-Trigger-After-Settle", "event1,event2"))
.andExpect(header().string("HX-Trigger-After-Swap", "event1,event2"));
}

@Test
public void testHeaderIsNotSetOnResponseIfHxTriggerNotPresent() throws Exception {
mockMvc.perform(get("/without-trigger"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,64 @@ public String methodWithHxTrigger() {
return "";
}

@GetMapping("/with-trigger-multiple-events")
@HxTrigger({ "event1", "event2" })
@ResponseBody
public String methodWithHxTriggerAndMultipleEvents() {
return "";
}

@GetMapping("/with-trigger-settle")
@HxTrigger(value = "eventTriggered", lifecycle = HxTriggerLifecycle.SETTLE)
@ResponseBody
public String methodWithHxTriggerAfterSettle() {
public String methodWithHxTriggerAndLifecycleSettle() {
return "";
}

@GetMapping("/with-trigger-swap")
@HxTrigger(value = "eventTriggered", lifecycle = HxTriggerLifecycle.SWAP)
@ResponseBody
public String methodWithHxTriggerAndLifecycleSwap() {
return "";
}

@GetMapping("/with-trigger-after-settle")
@HxTriggerAfterSettle("eventTriggered")
@ResponseBody
public String methodWithHxTriggerAfterSettle() {
return "";
}

@GetMapping("/with-trigger-after-settle-multiple-events")
@HxTriggerAfterSettle({ "event1", "event2" })
@ResponseBody
public String methodWithHxTriggerAfterSettleAndMultipleEvents() {
return "";
}

@GetMapping("/with-trigger-after-swap")
@HxTriggerAfterSwap("eventTriggered")
@ResponseBody
public String methodWithHxTriggerAfterSwap() {
return "";
}

@GetMapping("/with-trigger-after-swap-multiple-events")
@HxTriggerAfterSwap({ "event1", "event2" })
@ResponseBody
public String methodWithHxTriggerAfterSwapAndMultipleEvents() {
return "";
}

@GetMapping("/with-triggers")
@HxTrigger({ "event1", "event2" })
@HxTriggerAfterSettle({ "event1", "event2" })
@HxTriggerAfterSwap({ "event1", "event2" })
@ResponseBody
public String methodWithHxTriggers() {
return "";
}

@GetMapping("/updates-sidebar")
@HxUpdatesSidebar
@ResponseBody
Expand Down

0 comments on commit 512d937

Please sign in to comment.