Skip to main content
Dat 2. semester
Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

Thymeleaf

Thymeleaf

Thymeleaf er en template engine, som vi bruger til at generere HTML-sider fra vores Java-applikation.

En Thymeleaf-template er almindelig HTML med ekstra attributter, som begynder med th:.

Det betyder, at en HTML-side både kan ligne almindelig HTML og indeholde dynamisk indhold.

For at bruge Thymeleaf skal du have følgende i pom.xml (samme version som din Javalin)

 <dependency>
        <groupId>io.javalin</groupId>
        <artifactId>javalin-rendering-thymeleaf</artifactId>
        <version>7.2.3</version>
</dependency>

og følgende i din Javalin.create(..)

var app = Javalin.create(config -> {
    //.. anden konfiguration
    config.fileRenderer(new JavalinThymeleaf());
}).start(7070);

1. Templates

Thymeleaf templates ligger i:

src/main/resources/templates

En simpel template kan se sådan ud:

<!DOCTYPE html>
<html lang="da">
<head>
    <meta charset="UTF-8">
    <title>Min side</title>
</head>
<body>

<h1>Hej verden</h1>

</body>
</html>

Javalin kan rendre templaten:

ctx.render("index.html");

Når serveren renderer siden, bliver Thymeleaf-attributterne fortolket, og resultatet sendes som HTML til browseren.

2. Send data fra Java til Thymeleaf

Vi kan sende data til en template med ctx.attribute().

ctx.attribute("name", "Ada");
ctx.render("hello.html");

I templaten kan vi bruge attributten:

<h1 th:text="${name}">Navn</h1>

Browseren vil se:

<h1>Ada</h1>

3. th:text

th:text bruges til at sætte teksten i et HTML-element.

<p th:text="${name}">Navn</p>

Et objekt kan også bruges:

<p th:text="${user.username}">Brugernavn</p>
<p th:text="${user.email}">Email</p>

Thymeleaf kan bruge Java-objekters properties via deres getters.

4. Expressions med ${...}

Thymeleaf bruger ${...} til at hente værdier.

<p th:text="${name}"></p>

En property:

<p th:text="${user.username}"></p>

I praksis bruger vi normalt property-syntaksen:

${user.username}

5. Vis en liste med th:each

Hvis controlleren sender en liste:

List<User> users = userService.getAllUsers();

ctx.attribute("users", users);
ctx.render("users.html");

kan Thymeleaf iterere over listen:

<ul>
    <li th:each="user : ${users}"
        th:text="${user.username}">
        Brugernavn
    </li>
</ul>

user er den aktuelle User.

users er listen.

Det svarer lidt til en Java for-each:

for (User user : users) {
    // ...
}

Et tabel-eksempel:

<table>
    <thead>
    <tr>
        <th>Brugernavn</th>
        <th>Email</th>
    </tr>
    </thead>

    <tbody>
    <tr th:each="user : ${users}">
        <td th:text="${user.username}">Brugernavn</td>
        <td th:text="${user.email}">Email</td>
    </tr>
    </tbody>
</table>

6. Betingelser med th:if

Vi kan vise et element, hvis en betingelse er sand.

<p th:if="${user != null}">
    Du er logget ind.
</p>

Hvis user er null, bliver elementet ikke vist.

Vi kan også kontrollere en liste:

<p th:if="${not #lists.isEmpty(users)}">
    Der er brugere i systemet.
</p>

7. th:unless

th:unless er den modsatte situation.

<p th:unless="${user != null}">
    Du er ikke logget ind.
</p>

Det samme kan ofte skrives med th:if:

<p th:if="${user == null}">
    Du er ikke logget ind.
</p>

Brug den form, der gør betingelsen lettest at forstå.

Links til Javalin-routes kan skrives med th:href.

<a th:href="@{/users}">
    Brugere
</a>

Hvis URL’en indeholder en path-parameter:

<a th:href="@{/users/{id}(id=${user.id})}">
    Vis bruger
</a>

Hvis user.id er 42, bliver URL’en:

/users/42

Query-parametre kan også bygges:

<a th:href="@{/users(group=1,page=2)}">
    Vis brugere
</a>

Det giver:

/users?group=1&page=2

9. Billeder

Billeder, CSS og andre static files placeres typisk i public.

src/main/resources
├── public
│   ├── css
│   └── images
└── templates

Et billede kan refereres sådan:

<img th:src="@{/images/logo.png}" alt="Logo">

Hvis filen ligger her:

src/main/resources/public/images/logo.png

bliver den tilgængelig på:

/images/logo.png

10. CSS

Et stylesheet i:

src/main/resources/public/css/styles.css

kan inkluderes med:

<link rel="stylesheet"
      th:href="@{/css/styles.css}">

11. Request scope

Data, som kun skal bruges under den aktuelle request, kan lægges i request scope:

ctx.attribute("name", "Ada");
ctx.render("index.html");

I Thymeleaf:

<h1 th:text="${name}">Navn</h1>

Request scope lever kun under den aktuelle request.

Et typisk eksempel:

List<Task> tasks = taskService.getTasksForUser(user);

ctx.attribute("tasks", tasks);
ctx.render("tasks.html");

Derefter:

<div th:each="task : ${tasks}">
    <h2 th:text="${task.title}">Task</h2>
</div>

12. Session scope

Session scope bruges til data, som skal kunne bruges på tværs af flere requests.

Et klassisk eksempel er den bruger, der er logget ind:

ctx.sessionAttribute("user", user);

På en senere request kan vi hente brugeren:

User user = ctx.sessionAttribute("user");

I Thymeleaf kan sessionens attribut bruges sådan:

<p th:text="${session.user.username}">
    Brugernavn
</p>

Sessionen er derfor velegnet til eksempelvis:

  • den aktuelle bruger
  • login-status
  • andre oplysninger, der skal huskes mellem requests

13. Fra controller til template

Et typisk flow ser sådan ud:

config.routes.get("/tasks", ctx -> {

    User user = ctx.sessionAttribute("user");

    List<Task> tasks = taskService.getTasksForUser(user);

    ctx.attribute("tasks", tasks);
    ctx.render("tasks.html");
});

Og templaten:

<h1>Mine tasks</h1>

<ul>
    <li th:each="task : ${tasks}"
        th:text="${task.title}">
        Task
    </li>
</ul>

Flowet er:

HTTP request
Controller
Service
List<Task>
ctx.attribute(...)
Thymeleaf
HTML
Browser

14. Formularer

Thymeleaf kan bruges sammen med almindelige HTML-formularer.

<form action="/login" method="post">

    <label for="username">Brugernavn</label>
    <input type="text"
           id="username"
           name="username">

    <label for="password">Password</label>
    <input type="password"
           id="password"
           name="password">

    <button type="submit">
        Log ind
    </button>

</form>

Javalin kan hente værdierne:

String username = ctx.formParam("username");
String password = ctx.formParam("password");

Det vigtige er, at name i HTML matcher navnet i formParam().

15. Vis fejlbeskeder

Controlleren kan sende en fejlbesked til templaten:

ctx.attribute("error", "Forkert brugernavn eller password");
ctx.render("login.html");

I HTML:

<p th:if="${error != null}"
   th:text="${error}">
    Fejlbesked
</p>

Det gør det muligt at vise en fejl uden at lave en separat HTML-side.

16. Fragments

Fragments bruges til genbrug af HTML.

Et fragment kan for eksempel indeholde en navigation:

<nav th:fragment="navigation">
    <a th:href="@{/}">Forside</a>
    <a th:href="@{/tasks}">Tasks</a>
    <a th:href="@{/logout}">Log ud</a>
</nav>

Fragmentet kan indsættes i en anden template:

<div th:replace="~{fragments :: navigation}">
</div>

Hvis navigationen ændres, skal den derfor kun ændres ét sted.

17. Fragments med parametre

Et fragment kan modtage parametre.

Fragment:

<head th:fragment="head(title)">
    <title th:text="${title}">Titel</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>

Brug fragmentet:

<div th:replace="~{fragments :: head('Mine tasks')}"></div>

Her sendes teksten Mine tasks ind som parameteren title.

18. Natural Templates

En af Thymeleafs idéer er, at templates stadig er almindelig HTML.

Det betyder, at du kan skrive:

<h1 th:text="${name}">Navn</h1>

Hvis filen åbnes direkte i en browser uden Thymeleaf, ser man:

Navn

Når den renderes gennem Thymeleaf, bliver teksten erstattet med værdien af name.

Det kan gøre det lettere at arbejde med HTML, før backend-koden er færdig.

Fragments er en undtagelse, fordi browseren ikke selv kan fortolke Thymeleaf-fragments.

19. IntelliJ og Thymeleaf-variabler

IntelliJ kan ikke altid vide, hvilke attributter controlleren sender til templaten.

Derfor kan autocomplete være begrænset.

Man kan hjælpe IntelliJ med en kommentar:

<!--/*@thymesVar id="user" type="entities.User"*/-->

Derefter kan IntelliJ bedre forstå:

<span th:text="${user.username}"></span>

Det er kun en hjælp til IDE’en. Kommentaren har ingen betydning for Thymeleaf.

20. Hurtigt overblik

Jeg vil…Jeg bruger…
Vise en værdith:text
Gå gennem en listeth:each
Vise noget betingetth:if
Vise noget hvis betingelsen er falskth:unless
Lave et linkth:href
Indsætte et billedeth:src
Sende data fra Javactx.attribute()
Gemme data i sessionenctx.sessionAttribute()
Vise en templatectx.render()
Genbruge HTMLth:fragment + th:replace
Hente form-datactx.formParam()
Hente query-parametrectx.queryParam()

Thymeleaf tager sig af præsentationen.

Controlleren henter data og beslutter, hvilken template der skal vises.

Servicen tager sig af programmets logik.


Typisk flow

Browser
Controller
Service
ctx.attribute(...)
Thymeleaf
HTML
Browser