Dialetos do Thymeleaf

O Thymeleaf é uma engine de templates que permite a criação de páginas HTML dinâmicas e usa o conceito chamado de dialetos para adicionar recursos específicos ao HTML. Você consegue identificar um dialeto quando vê um atributo HTML que começa com th:. Por exemplo, o atributo th:text é um dialeto que permite a exibição de dados dinâmicos na página HTML.

Os dialetos do Thymeleaf permitem que receber valores chamados de expressões e são classificadas em 5 tipos:

Expressões de variáveis

As expressões de variáveis são usadas para exibir valores dinâmicos na página HTML. Por exemplo, se você tem uma variável chamada nome e quer exibir o seu valor na página HTML, você pode usar a expressão ${nome} dentro do dialeto th:text. O resultado final ficaria

1
<p th:text="${nome}">Nome</p>

Expressões de seleção

As expressões de seleção são como as expressões de variáveis, mas são usadas para selecionar um atributo de um objeto. O objeto em que as expressões de seleção são aplicadas são aqueles definidos pelo dialeto th:object. Por exemplo, se você tem um objeto chamado produto e quer exibir o seu atributo nome na página HTML, você pode usar a expressão *{nome} dentro do dialeto th:text assim que tiver mapeado o objeto produto com o dialeto th:object. O resultado final ficaria

1
<div th:object=${produto}>
2
<p th:text="*{nome}"></p>
3
</div>

Expressões de mensagens

São utilizadas para exibir mensagens geralmente chamadas de externalização de texto, internacionalização ou i18n. Elas permitem acessar um arquivo de propriedades que contém as mensagens e exibi-las na página HTML. Por exemplo, se você tem um arquivo de propriedades chamado messages.properties e quer exibir a mensagem site.welcome na página HTML, você pode usar a expressão #{site.welcome} dentro do dialeto th:text. O resultado final ficaria

1
<p th:text="#{site.welcome}">Hello</p>

O arquivo de propriedades deve estar na pasta src/main/resources e deve ter a extensão .properties. Para saber mais sobre arquivos de propriedades, acesse este link.

São utilizados para construir URLs de acordo com o contexto que está sendo executado. Podem ser de diferentes tipos: absolutos, relativos, contextuais, etc. Por exemplo, se você quer construir uma URL para a página index.html, você pode usar a expressão @{/index.html} dentro do dialeto th:href. O resultado final ficaria

1
<a th:href="@{/index.html}">Home</a>

Expressões de fragmentos

Os fragmentos permitem a movimentação de pedaços de código HTML em diferentes páginas. Através deles é possível reaproveitar o código, usar o código HTML como argumento de outra tag HTML e até mesmo passar parâmetros para o fragmento.

Por exemplo, imagine que você tem uma página que contém um rodapé e quer reaproveitá-lo em outras páginas, ou seja, você quer separar esse trecho de marcação (HTML) em um arquivo separado e incluí-lo em outras páginas.

Você pode criar um arquivo chamado rodape.html e colocar o código HTML do rodapé dentro dele. Depois, você pode incluir esse arquivo em outras páginas usando a expressão ~{rodape} dentro do dialeto th:insert. O resultado final ficaria

1
<div th:insert="~{rodape}"></div>

A sintaxe das expressões de fragmento podem ter três diferentes formas:

Ainda é possível incluir fragmentos que não fazem uso do atributo th:fragment. Para isso, você deve referenciar o trecho de código que deseja incluir utilizando o atributo id da marcação HTML.

1
<div id="rodape">
2
<p>Este é o rodapé</p>
3
</div>

Depois, você pode incluir esse trecho de código em outra página usando a expressão ~{#rodape} dentro do dialeto th:insert. O resultado final ficaria

1
<div th:insert="~{footer :: #rodape}"></div>

É importante compreender também a diferença entre os dialetos th:insert e th:replace. Os nomes tentam deixar bastante claro a diferença entre eles em que o insert faz a inserção do código HTML no local especificado e o replace faz a substituição do código HTML no local especificado. O exemplo abaixo ilustra bem a diferença entre eles.

1
<footer th:fragment="rodape">
2
<p>Este é o rodapé</p>
3
</footer>
1
<body>
2
<div th:insert="~{footer :: rodape}"></div>
3
<div th:replace="~{footer :: rodape}"></div>
4
</body>

O resultado final ficaria

1
<body>
2
<div>
3
<footer>
4
<p>Este é o rodapé</p>
5
</footer>
6
</div>
7
<footer>
8
<p>Este é o rodapé</p>
9
</footer>
10
</body>

Veja que o dialeto th:insert inclui o código HTML dentro da tag div e o dialeto th:replace substitui o código HTML da tag div pelo código HTML do rodapé.

Herança de Layout

A herança de layout é um recurso que permite a criação de um template base que pode ser herdado por outros templates. Por exemplo, você pode criar uma página chamada app.html e colocar o código HTML que se repete em todas as páginas dentro dela. Depois, você pode criar outros páginas que herdam o template base e incluir o código HTML específico de cada página.

Vamos usar a página home.html como layout base. Para isso precisamos definir a tag html como sendo um fragmento e que receberá parâmetros para que possamos herdar esse layout em outros arquivos. O resultado final ficaria

1
<!DOCTYPE html>
2
<html lang="pt-br" th:fragment="layout(content)">
3
<head>
4
<meta charset="UTF-8">
5
<link rel="stylesheet" href="/css/pico.min.css">
6
<title>Home</title>
7
</head>
8
<body class="container">
9
<main th:replace="${content}"></main>
10
</body>
11
</html>

O importante no código é o atributo th:fragment que define o nome do fragmento e o parâmetro content que será usado para receber o conteúdo das outras páginas. Perceba que logo mais abaixo o parâmetro content é usado para substituir o conteúdo da tag main através do dialeto th:replace. Ou seja, o conteúdo recebido por parâmetro será substituído no local onde o parâmetro ${content} está sendo declarado.

Agora que temos um template base, podemos criar outras páginas que herdam esse template. Vamos aproveitar a página de listar usuários user/list.html. O resultado final ficaria

1
<html th:replace="~{home :: layout(~{::body})}">
2
<body>
3
<h1>Users</h1>
4
<table>
5
<tr>
6
<th>ID</th>
7
<th>Email</th>
8
<th>Actions</th>
9
</tr>
10
<tr th:each="user : ${users}">
11
<td th:text="${user.id}"></td>
12
<td th:text="${user.email}"></td>
13
<td>
14
<a th:href="@{/users/show(id = ${user.id})}">Show</a> |
15
<a th:href="@{/users/edit(id = ${user.id})}">Edit</a> |
16
<a th:href="@{/users/delete(id = ${user.id})}">Delete</a>
17
</td>
18
</tr>
19
</table>
20
<a th:href="@{/users/create}">New user</a>
21
</body>
22
</html>

Perceba que omitimos diversas tags HTML pois elas já estão declaradas no template base. Agora é necessário herdar o template base, para isso utiliza-se o dialeto th:replace com o valor ~{home :: layout(~{::body})}. Aonde, home é o nome do arquivo que contém o template base e layout(..) é o nome da função declarada no template base, e ~{::body} é valor que sera passado como parâmetro para a função layout(..), e nesse caso representa o seletor de tag body da página em questão. Ou seja, o conteúdo do body da página list.html será substituído no local onde o parâmetro ${content} está sendo declarado no template base.

Você pode replicar o mesmo conceito para as páginas create, edit e show para praticar.