vendredi 17 décembre 2021

Le patron décorateur avec Thymeleaf

Dans le précédent article de thymeleaf, nous avons utilisé l'approche par défaut des templates. Nous avions une page principal, product et about. Pour chacune d'elle, nous avions un fragment menu, footer. Tous ces pages avaient une structure similaire. 

Nous allons maintenant utiliser une approche différente. Nous allons utiliser le patron décorateur.

Nous allons utiliser le même exemple, mais avec cette approche.Le header sera mis dans un fragment cette fois.

Les sources sont disponibles sur github

Le fragment header

<head th:fragment="my-header">
<title>Good Thymes Virtual Grocery</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" media="all" href="../../css/main.css" th:href="@{/css/main.css}" />
</head>

 

Layout Decorator

Nous allons créer un fichier main-layout avec les sections similaires au niveau des fragments.

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout">
<head th:replace="fragments/header :: my-header">
<body>
<div th:replace="fragments/menu :: my-menu"></div>
<div class="container" id="mainContent">
<div layout:fragment="content"></div>
</div>
<div th:replace="fragments/footer :: my-footer"></div>
</body>
</html>

Les templates

Le fichier products ressemblera a 

<!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="layout/mainLayout">
<head>
<title>Products</title>
</head>
<body>
<div layout:fragment="content">
<table>
<tr>
<td>Id</td>
<td>Name</td>
<td>Description</td>
<td>Price</td>
</tr>
<tr th:each="product: ${products}">
<td th:text="${product.id}"/>
<td th:text="${product.name}"/>
<td th:text="${product.description}"/>
<td th:text="${product.price}"/>
</tr>
</table>
</div>
</body>
</html>
 

Le fichier about ressemblera a 

<!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="layout/mainLayout">
<head>
<title>About</title>
</head>
<body>
<div layout:fragment="content">
<p>Grocery exist since 1985</p>
</div>
</body>
</html>

 
Il suffit d'indiquer dans nos templates  dans la section layout:decorate celui qu'on désire utiliser.


Le patron décorateur n'est pas l'approche standard de thymeleaf. Cependant, c'est celle qui demeure la plus efficace pour une application de grande envergure.

jeudi 16 décembre 2021

La puissance des fragments avec Thymeleaf

Nous allons traiter une des particularités de Thymleaf, les fragments. Nous avons vue jusqu'à maintenant les templates.

Nous utiliserons spring boot et la structure du projet sera. Les sources sont disponible ici.

 

 

Un template peut être vue comme une page html, javascript, css. 

Par exemple, dans un fichier index.html

<!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">

  <head>
    <title>Good Thymes Virtual Grocery</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <link rel="stylesheet" type="text/css" media="all" 
          href="../../css/main.css" th:href="@{/css/main.css}" />
  </head>

  <body>
    <p th:text="#{home.welcome}">Welcome to our grocery store!</p>
    <p>Today is: <span th:text="${today}">13 February 2011</span></p>
  </body>

</html>
 

Un fragment est une portion de code qui peut être réutilisé dans un autre fragment ou template. Ils peuvent être accessible via un nom, id ou bien par un selector (comme un peu jquery).

Dans un fichier html, par exemple: menu.html

    <ul th:fragment="my-menu">
        <li><a th:href="@{/}">Home</a></li>
        <li><a th:href="@{/products}">Products</a></li>
        <li><a th:href="@{/about}">About</a></li>
    </ul>
    Ce Texte sera affiché aussi.   

Nous pourrions avoir un template pour products et un autre pour about et ce fragment serait utilisé dans chacun d'eux.

Pour utiliser ce fragment dans le fichier index.html

<!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">

  <head>
    <title>Good Thymes Virtual Grocery</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <link rel="stylesheet" type="text/css" media="all" 
          href="../../css/main.css" th:href="@{/css/main.css}" />
  </head>

  <body>
    <div th:replace="fragments/menu.html"/> 
    <p>
<span th:text="#{today}">Today</span>
<span th:text="${today}">16 december 2021</span>
</p> </body> </html>

Le nom du fragment a été utilisé, cette approche est utile uniquement si le fichier html contient totalement ce qui nous intéresse, car tous le fichier sera affiché.

Si nous désirons qu'une portion il faut utiliser un sélector, cette opération se fait via les deux points.

Nous devons préciser l'emplacement du fragment / le nom du fichier du fragment :: le nom du fragment.

Ce qui donne dans notre cas

fragments/menu :: my-menu
 

Un autre fragment a été ajouté pour le footer. Le fichier donne avec les fragments.


<!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">

  <head>
    <title>Good Thymes Virtual Grocery</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <link rel="stylesheet" type="text/css" media="all" 
          href="../../css/main.css" th:href="@{/css/main.css}" />
  </head>

  <body>
    <div th:replace="fragments/menu :: my-menu"/> 
    <p>
<span th:text="#{today}">Today</span>
<span th:text="${today}">16 december 2021</span>
</p>
    <div th:replace="fragments/footer :: my-footer"/>  
  </body>

</html>

Pour le fichier about, il pourrait ressembler à

 

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
   <title>Good Thymes Virtual Grocery</title>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
   <link rel="stylesheet" type="text/css" media="all"
href="../../css/main.css" th:href="@{/css/main.css}"/>
</head>
<body>
   <div th:replace="fragments/menu.html"/>
   <p>Grocery exist since 1985</p>
   <div th:replace="fragments/footer.html"/>
</body>
</html>

Pour le fichier products, la partie backend nous retournerais une liste de produit.

<!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">

<head>
    <title>Good Thymes Virtual Grocery</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <link rel="stylesheet" type="text/css" media="all"
          href="../../css/main.css" th:href="@{/css/main.css}"/>
</head>

<body>
<div th:replace="fragments/menu.html"/>

<table>
    <tr>
        <td>Id</td>
        <td>Name</td>
        <td>Description</td>
        <td>Price</td>
    </tr>
    <tr th:each="product: ${products}">
        <td th:text="${product.id}"/>
        <td th:text="${product.name}"/>
        <td th:text="${product.description}"/>
        <td th:text="${product.price}"/>
    </tr>
</table>
<div th:replace="fragments/footer.html"/>
</body>

</html>
 

C'est assez simple. Il suffit de créer à chaque fois une page html et d'insérer les différents fragments qui nous intéresse. 

Dans notre exemple, la partie head pourrait être un fragment.

Dans une application web complexe, il faut bien cerner les portions qui peuvent être réutiliser et les définir comme des fragments pour éviter la réécriture de code et faciliter la maintenance.

Cette approche d'utiliser les templates est celle par défaut au sein de thymeleaf. Lorsque le site possède plusieurs dizaines, voir centaines de pages, d'autre possibilités sont offerte pour éviter de devoir réécrire les sections redondantes à une multitude de page. Nous la verrons dans un prochain articles.