Reactor with spring
Linking a spring event to a Flux
Create a sink
// The exact config depends on the needs
Sinks.Many<Event> sink = Sinks.many().replay().limit(1);
@EventListener(Event.class)
public void onTick(Event event) {
sink.tryEmitNext(event);
}
public Flux<Event> someMethod() {
return sink.asFlux();
}
HTMX with Spring boot and flux
Configure maven
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>nz.net.ultraq.thymeleaf</groupId>
<artifactId>thymeleaf-layout-dialect</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator</artifactId>
<version>0.46</version>
</dependency>
<dependency>
<groupId>org.webjars.npm</groupId>
<artifactId>htmx.org</artifactId>
<version>1.9.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Basic template configuration
main.html
<!DOCTYPE html>
<html th:lang="|${#locale.language}-${#locale.country}|"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta content="width=device-width, initial-scale=1" name="viewport">
<title></title>
<!-- <link-->
<!-- rel="stylesheet"-->
<!-- href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"-->
<!-- />-->
</head>
<body>
<main layout:fragment="content">
</main>
<script th:src="@{/webjars/htmx.org/dist/htmx.min.js}" type="text/javascript"></script>
<script th:src="@{/webjars/htmx.org/dist/ext/sse.js}" type="text/javascript"></script>
<th:block layout:fragment="script-content">
</th:block>
</body>
</html>
index.html
<!DOCTYPE html>
<html lang="en"
layout:decorate="~{layout/main}"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<body>
<div layout:fragment="content">
... content should be here ...
</div>
</body>
</html>
Push from the backend
@GetMapping("/time")
public Flux<ServerSentEvent<String>> progress() {
return timeFlux
.map(e -> ServerSentEvent.<String>builder().data("<div>"+ e + "</div>").build());
}
<div hx-ext="sse" sse-connect="/time" sse-swap="message">
--- This will be replaced by the response of /time ---
</div>
Get data at page load (lazy loading)
<div hx-get="/versions" hx-trigger="load">
--- This will be replaced by the response of /time ---
</div>
Render the template on demand
import org.thymeleaf.TemplateEngine;
@Autowired
private final TemplateEngine templateEngine;
private String render(String template, Map<String, Object> parameter) {
IContext context = new Context(Locale.getDefault(), parameter);
return templateEngine.process(template, context).replace("\r", "").replace("\n", "");
}
Clear content on sse connection error
<script>
function clearcontent(elementID) {
document.getElementById(elementID).innerHTML = "<H1>Connection error with the backend!</H1>";
}
</script>
<script>
document.body.addEventListener('htmx:sseError', function (evt) {
clearcontent('monitoring-sse')
});
</script>