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*{...}: Expressões de seleção#{...}: Expressões de mensagens (internacionalização)@{...}: Expressões de links (URLs)~{...}: Expressões de fragmentos
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
<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
<div th:object=${produto}>
<p th:text="*{nome}"></p>
</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
<p th:text="#{site.welcome}">Hello</p>
O arquivo de propriedades deve estar na pasta
src/main/resourcese deve ter a extensão.properties. Para saber mais sobre arquivos de propriedades, acesse este link.
Expressões de links
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
<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
<div th:insert="~{rodape}"></div>
A sintaxe das expressões de fragmento podem ter três diferentes formas:
~{nome-do-arquivo}: Inclui o arquivo inteiro.~{nome-do-arquivo :: nome-do-fragmento}: Inclui apenas o fragmento do arquivo especificado no arquivo de template. Logo, o arquivo de template deve ter um fragmentoth:fragmentcom o nome especificado.~{::nome-do-fragmento}: Insere o fragmento do arquivo de template atual. Ou seja, o arquivo de template deve ter um fragmentoth:fragmentcom o nome especificado.
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.
<div id="rodape">
<p>Este é o rodapé</p>
</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
<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.
<footer th:fragment="rodape">
<p>Este é o rodapé</p>
</footer>
<body>
<div th:insert="~{footer :: rodape}"></div>
<div th:replace="~{footer :: rodape}"></div>
</body>
O resultado final ficaria
<body>
<div>
<footer>
<p>Este é o rodapé</p>
</footer>
</div>
<footer>
<p>Este é o rodapé</p>
</footer>
</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
<!DOCTYPE html>
<html lang="pt-br" th:fragment="layout(content)">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="/css/pico.min.css">
<title>Home</title>
</head>
<body class="container">
<main th:replace="${content}"></main>
</body>
</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
<html th:replace="~{home :: layout(~{::body})}">
<body>
<h1>Users</h1>
<table>
<tr>
<th>ID</th>
<th>Email</th>
<th>Actions</th>
</tr>
<tr th:each="user : ${users}">
<td th:text="${user.id}"></td>
<td th:text="${user.email}"></td>
<td>
<a th:href="@{/users/show(id = ${user.id})}">Show</a> |
<a th:href="@{/users/edit(id = ${user.id})}">Edit</a> |
<a th:href="@{/users/delete(id = ${user.id})}">Delete</a>
</td>
</tr>
</table>
<a th:href="@{/users/create}">New user</a>
</body>
</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.