Thymeleaf est un moteur de template serveur régulièrement utilisé le monde Java, principalement avec le framework Spring. Dans cet article, nous présentation quelques architectures possibles avec ce framework ainsi que l'utilisation de Htmx afin de rendre les applications thymeleaf plus dynamiques.
Architecture d'application
HDA
L'augmentation du temps de développement, la complexité des frameworks, courbe d'apprentissage élevé, maintenance onéreuse ont fait reculé certains.
Hypermedia Driven Application combine la facilité des multiples pages application (mpa) avec l'expérience utilisateur des single page application (spa).
Une panoplie de librairie existe pour apporter l'expérience utilisateur à des frameworks orientés serveur.
unpoly, htmx, alpine, hyperscript... permettent via l'ajout de mot clé dans le html de rendre des applications utilisant des framework serveurs: jsf, jsp, thymeleaf... plus dynamique, d'éviter de charger la page au complet..
Htmx
Vidéo d'une présentation de thymeleaf avec htmx
Une autre présentation
https://www.youtube.com/watch?v=38WAVRfxPxI
Une liste d'exemple est disponible via ce lien.
Éviter le rafraîchissement de la page
<!DOCTYPE html>
<html lang="en">
<head th:replace="~{fragments/css :: headcss}">
<meta charset="UTF-8">
<title>test</title>
</head>
<body class="container">
<div th:replace="~{fragments/navbarmenu :: menu}"/>
<section id="main" class="fade-me-out">
<div id="modals-here"></div>
</section>
<div th:replace="~{fragments/jsscripts :: jsscripts}"/>
</body>
</html>
Fragment pour le css
<!DOCTYPE html>
<html lang="en">
<head th:fragment="headcss">
<title>Generic form UI</title>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" th:href="@{/css/bootstrap.min.css}" href="/css/bootstrap.min.css" />
<link rel="stylesheet" type="text/css" th:href="@{/css/datepicker-bs5.min.css}" href="/css/datepicker-bs5.min.css" />
</head>
<body>
</body>
</html>
Fragment pour le javascript
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>js scripts</title>
</head>
<body>
<div th:fragment="jsscripts">
<script type="text/javascript" th:src="@{/js/genericui.js}" src="/js/genericui.js"></script>
<script type="text/javascript" th:src="@{/js/bootstrap.bundle.min.js}" src="/js/bootstrap.bundle.min.js"></script>
<script type="text/javascript" th:src="@{/js/datepicker-full.min.js}" src="/js/datepicker-full.min.js"></script>
<script type="text/javascript" th:src="@{/js/htmx.min.js}" src="/js/htmx.min.js"></script>
<script type="text/javascript" th:src="@{/js/inputmask.min.js}" src="/js/inputmask.min.js"></script>
<script type="text/javascript" th:src="@{/js/bootstrap-validation.js}" src="/js/bootstrap-validation.js"></script>
<script type="text/javascript">
document.addEventListener('htmx:afterRequest', function(evt) {
Inputmask().mask(document.querySelectorAll("input"));
const matchesDate = document.getElementsByClassName("date1");
for (let i = 0; i < matchesDate.length; i++) {
const datepicker = new Datepicker(matchesDate[i], {
buttonClass: 'btn',
});
}
});
</script>
</div>
</body>
</html>
Fragment pour le menu
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>NavBar</title>
</head>
<body>
<nav th:fragment="menu" class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" th:href="@{/index2}">Norenda</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" th:href="@{/index}">Index</a>
</li>
<li class="nav-item">
<a class="nav-link active" aria-current="page" hx-push-url="true" hx:get="@{/modal}" hx-target="#main" hx-swap="innerHTML" hx-trigger="click">Modal</a>
</li>
<li class="nav-item">
<a class="nav-link" hx-push-url="true" hx:get="@{/success}" hx-target="#main" hx-swap="innerHTML swap:1s" hx-trigger="click">Succes</a>
</li>
<li class="nav-item">
<a class="nav-link" hx-push-url="true" hx:get="@{/editor}" hx-target="#main" hx-swap="innerHTML swap:1s" hx-trigger="click">Editeur</a>
</li>
</ul>
<form class="d-flex" role="search" hx:post="@{/search}" hx-target="#main" hx-swap="innerHTML">
<input class="form-control me-2" type="search" placeholder="Recherche" aria-label="Search">
<button class="btn btn-outline-success" type="submit">Recherche</button>
</form>
</div>
</div>
</nav>
</body>
</html>
On mentionne ici quel sera la cible du fragment retourné, où ira le contenu
On mentionne ici qu'on remplace le contenu de la cible par celui du fragment, il serait possible de l'ajouter avant, après
Animation
Conclusion
Htmx peut vous permettre de rendre encore plus dynamique votre application qui utilise un framework orienté serveur à une fraction du prix en termes de temps, argent et apprentissage. Pour encore plus de fonctionnalité, il faut regarder unpoly.
Vous pouvez télécharger les sources du projet ici.