CDI producer for HTMX headers (in Qute templates)
At €€€-dayjob I’m in the fortunate position to be able to write a HTMX application using Quarkus and Qute’s HTML templates. As such I had the need to identify if a request was performed regularly or through a HTMX call. With a small amount of CDI code this was achieved without much fuzz.
An example of its use in a template:
{#if cdi:htmx.isHtmxRequest}
<small hx-swap-oob="outerHTML" id="lastUpdated">{lastUpdated}</small>
{/if}
The example above will include the <small>
HTML fragment to be swapped out-of-bounds
in the response when the request was performed using HTMX, and omit the <small>
fragment when the Qute template is rendered using a normal, non-HTMX request.
HTMX will set the HX-Request: true
header in the request to your server when it
issues a request. Using the JAX-RS @HeatherParam("XX-Request)
annotation we can
retrieve this value for use in our detector.
The following CDI producer that ensures the proper HTMX headers are retrieved
and made available through a request scoped Htmx
object.
Design considerations:
Htmx
can’t be a Java record, as those arefinal
and CDI requires beans not to be suchHtmx
must be a top level class, or a static one as CDI requires a default constructor- you can’t just produce a
boolean
orBoolean
as those are not subclasseable (because CDI) - because Quarkus uses ARC as the CDI implementation, they advise (require?) injections to be package private
- to retrieve CDI beans in your Qute template, the bean must be
@Named
- you can’t use
@HeaderParam
annotations because those are unknown to CDI
import jakarta.enterprise.context.RequestScoped;
import jakarta.enterprise.inject.Produces;
import jakarta.inject.Named;
import jakarta.inject.Singleton;
import jakarta.ws.rs.HeaderParam;
@Singleton
public class HtmxHeaderProducer {
public static class Htmx {
private boolean htmxRequest;
private boolean htmxBoosted;
Htmx(boolean htmxRequest, boolean htmxBoosted) {
this.htmxRequest = htmxRequest;
this.htmxBoosted = htmxBoosted;
}
public boolean isHtmxRequest() {
return htmxRequest;
}
public boolean isHtmxBoosted() {
return htmxBoosted;
}
}
@Inject
HttpHeaders headers;
@Produces
@Named("htmx")
@RequestScoped
public Htmx getHtmx() {
var hxRequest = headers.getHeaderString("HX-Request");
var hxBoosted = headers.getHeaderString("HX-Boosted");
return new Htmx("true".equals(hxRequest), "true".equals(hxBoosted));
}
}
If you want provide more HTMX-header functionality, it should be fairly easy to implement those in this CDI producer. Enjoy!