<a href="https://www.javaland.eu">
Javaland
</a>
Senior Software Engineer @adesso Business Area Manufacturing
Java, Web
👨👩👧👦 🏊♂️ 🚴 🏃
JHipster Core Team Member
JUG Paderborn Orga
Geschichte
sportlich
Promis
kulinarisch
…😔
Hypertext & Hypermedia
HTMX
Thymeleaf, Spring Boot and HTMX
How old are the terms hypermedia and hypertext? |
Hypertexts: new forms of writing, appearing on computer screens, that will branch or perform at the reader’s command. A hypertext is a non-sequential piece of writing; only the computer display makes it practical.
actually, it all goes even back to the 40ties and the memex 😱 |
essential is the non-linear branching model
links between hypermedia documents
HTML is the most widely used form of hypermedia
Invented by Tim-Berners Lee in the 90ties
Today more or less "everywhere"
only two elements: anchor and form
<a href="https://www.javaland.eu">
Javaland
</a>
What does it mean and do?
Show Javaland
is clickable
Issue a request to https://www.javaland.eu
Replace the entire screen with the response body
<form action="/signup" method="post">
<input type="text" name="email"
placeholder="Enter Email To Sign Up..."/>
<button>Sign Up</button>
</form>
Show a text input and a Sign Up
button
When form is submitted issue a Post request to the server
Replace the entire screen with the response body[1]
Anchor and Form have been enough to make the web grow
Json Data API + SPA are not hypermedia
HTML has stopped evolving as a hypermedia
What would have happened if HTML has evolved as hypermedia? 🤔
Why should only <a> and <form> be able to make HTTP requests? |
Why should only click & submit events trigger them? |
Why should only GET & POST methods be available? |
Why should you only be able to replace the entire screen? |
HTMX is the sucessor of intercooler.js. Intercooler.js started 2013/2014 with exact the same approach, but didn’t get much traction. Somehow this is different now with HTMX. 🤷 |
<button hx-get="/contacts/1"
hx-target="#contact-ui">
Fetch Contact
</button>
No explicit scripting, JSON handling
declarative approach
Response is supposed to be HTML again
<head>
<script src="https://unpkg.com/htmx.org@1.9.2"
integrity="sha384-L6OqL9pRWyyFU3+/bjdSri+iIphTN/bvYyM37tICVyOJkWZLpP2vGn6VUEXgzg6h"
crossorigin="anonymous"></script>
</head>
Any element can make HTTP requests (hx-get|delete|post|put|patch)
Any element can trigger HTTP requests (hx-trigger)
Any HTTP method can be used
Any element can be replaced (hx-target, hx-swap)
Typical patterns/use cases for web applications
<html>
...
<body hx-boost="true">
...
</body>
</html>
<form id="new-todo-form" th:action="@{/}"
method="post" th:object="${item}">
<input id="new-todo-input" class="new-todo"
autocomplete="false"
name="title"
th:field="*{title}"
hx-target="#todo-list"
hx-swap="beforeend"
hx-post="/"
hx-trigger="keyup[key=='Enter']"
>
</form>
Keep the normal form, such that it even works without Javascript
<th>
<div hx-get="/manager/users?sort=username,desc
&size=10&page=0&query="
hx-target="#users-table-wrapper"
hx-push-url="true">
</th>
<a hx-get="/manager/users?sort=username,ASC
&size=10&page=0&query="
hx-target="#users-table-wrapper"
hx-push-url="true">1</a>
<a hx-get="/manager/users?sort=username,ASC
&size=10&page=1&query="
hx-target="#users-table-wrapper"
hx-push-url="true">2</a>
...
<a href="/manager/users/57/change-status"
title="Benutzerstatus ändern"
hx-get="/manager/users/57/change-status"
hx-target="closest td"
hx-swap="outerHTML">deaktiviert</a>
@GetMapping(value = "/manager/users/{id}/change-status",
produces = "text/html-partial",
headers = "HX-Request")
public String changeStatus_htmx(@PathVariable Long id,
Pageable pageable, String query, Model model) {
User user = userManagementService.findOneById(id);
userManagementService.changeStatus(user);
createUsersPageModel(model, pageable, Optional.ofNullable(query));
model.addAttribute("user", user);
return "usermanagement/fragments/user-row-contents :: user-status";
}
<span th:fragment="active-items-count"
id="active-items-count"
class="todo-count"
hx-get="/active-items-count"
hx-swap="outerHTML"
hx-trigger="itemAdded from:body">
...
</span>
@PostMapping(headers = "HX-Request")
public String htmxAddTodoItem(TodoItemFormData formData,
Model model, HttpServletResponse response) {
TodoItem item = repository.save(...);
model.addAttribute("item", toDto(item));
response.setHeader("HX-Trigger", "itemAdded");
return "fragments :: todoItem";
}
@GetMapping
public String index(Model model, Pageable pageable) {
Slice<Location> slice = repository.findAll(pageable);
model.addAttribute("slice", slice);
model.addAttribute("items", slice.getContent());
return "endless-scrolling/index";
}
@GetMapping
@HxRequest
public String items(Model model, Pageable pageable) {
Slice<Location> slice = repository.findAll(pageable);
model.addAttribute("slice", slice);
model.addAttribute("items", slice.getContent());
return "endless-scrolling/table :: list";
}
<th:block th:each="item, iStat : ${items}">
<th:block th:if="${!iStat.last}">
<li th:text="${item.name}"></li>
</th:block>
<th:block th:if="${iStat.last}">
<li
hx:get="@{/endless-scrolling?sort={sort}&page={page}&size={size}(page=${slice.getNumber + 1},size=5,sort='name,desc')}"
hx-trigger="intersect once"
hx-swap="afterend"
th:text="${item.name}"
></li>
</th:block>
</th:block>
Modal dialogs
Inline editing/validation
Active search
lazy loading
Endless scrolling
Websockets, Server Send Events
…
You can use any template language you like
Fragments makes it very convinient to create partial html
Usually you use fragments already and can reuse them
<li th:fragment="todoItem(item)">
...
</li>
@GetMapping(value = "...",
produces = "text/html-partial",
headers = "HX-Request")
...
return "fragments :: todoItem";
...
You can of course use Quarkus, Micronaut "what ever you like"
Good integration with thymeleaf
@HxRequest
@HxTrigger("itemAdded")
Out Of Band Swaps ⁉️
Return multiple fragments
Update multiple elements on the page
e.g. Number of todo items (instead doing a second request)
Hypermedia driven applications are a viable alternative to omnipresent SPAs
Reduce complexity
At least opens the way to progressive enhancement
Form based ("enterprise") applications
CRUD frontends
e-commerce frontends
Something like google docs, maps
Heavy interaction
Offline requirements
But for sure you can combine the two approaches
Allerdings gibt es so viele Aspekte, die HTMX für größere, komplexere Projekte vollkommen ungeeignet machen: HTMX missachtet bewährte Architekturprinzipien, widerspricht gängigen Standards, missachtet die Trennung von UI und Daten und legt dem Backend Restriktionen auf, weil das Frontend es so verlangt. Es lässt sich nicht vernünftig debuggen oder testen.
HTMX zwingt Sie zudem dazu, sich eine weitere framework- oder bibliotheksspezifische, proprietäre Syntax aneignen zu müssen. Die von HTMX eingeführten Attribute und deren Wertestrukturen folgen nämlich ebenfalls keinem Standard.
Wenn das Ergebnis eines HTTP-Requests direkt als Inner-HTML gerendert wird, wie verhält es sich dann mit APIs? Schließlich muss aus dem JSON, XML oder einem anderen Datenformat, das eine API liefert, etwas entstehen, das in HTML darstellbar ist. Es müsste also eine Art Templating oder Ähnliches geben, oder? Doch das ist nicht der vorgesehene Weg.