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 fragmentosAs 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>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>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/resourcese 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>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:
~{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 fragmento th:fragment com o nome especificado.~{::nome-do-fragmento}: Insere o fragmento do arquivo de template atual. Ou seja, o arquivo de template deve ter um fragmento th:fragment com 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.
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é.
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.