banner
jzman

jzman

Coding、思考、自觉。
github

Spring Boot Series: Thymeleaf Template Layout

PS: If you can't discipline yourself for now, then keep yourself busy.

The previous articles attempted interface development, Thymeleaf templates, and their commonly used syntax. You can read the earlier articles before this one:

The main purpose of Thymeleaf template layout is to better organize the front-end pages, primarily through Thymeleaf-related syntax for layout. The main content is as follows:

  1. Referencing template fragments
  2. Fragment expression syntax
  3. Parameterized template fragments
  4. Removing template fragments
  5. Template layout inheritance

Referencing Template Fragments#

Using th:fragment allows you to define layout fragments for other pages to reference. The template fragment is defined in footer.html as follows:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Footer</title>
</head>
<body>
<!--Define layout fragment-->
<div th:fragment="copy">
    &copy; 2020 Gōngxíngzhī
</div>
</body>
</html>

The above defines a fragment named copy, which can be included using th:insert, th:replace, and th:include. Note that th:include is no longer recommended after Thymeleaf 3.0. The template fragment is referenced in home.html as follows:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Template Layout.</title>
</head>
<body>
    <!--Include template fragment-->
    <div th:insert="~{footer::copy}"></div>
</body>
</html>

Run the project and check http://localhost:8080/home, the result is as follows:

© 2020 Gōngxíngzhī
© 2020 Gōngxíngzhī

Next, let's look at the differences between th:insert, th:replace, and th:include, using all three methods to include the fragment named copy as follows:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Template Layout.</title>
</head>
<body>
    <!--Differences between th:insert, th:replace, and th:include-->
    <p>---Differences between th:insert, th:replace, and th:include---</p>
    <!--Directly insert template fragment-->
    <div id="insert" th:insert="~{footer::copy}">insert</div>
    <!--Directly replace current fragment-->
    <div id="replace" th:replace="~{footer::copy}">replace</div>
    <!--Directly insert the content of the specified fragment into the current fragment-->
    <div id="include" th:include="~{footer::copy}">include</div>
</body>
</html>

In the above code, the three divs are set with corresponding ids as insert, replace, and include. After running the project, the source code viewed in the browser is as follows:

<!--...-->

<!--Differences between th:insert, th:replace, and th:include-->
<p>---Differences between th:insert, th:replace, and th:include---</p>
<div id="insert">
	<div>
		&copy; 2020 Gōngxíngzhī
	</div>
</div>
<div>
	&copy; 2020 Gōngxíngzhī
</div>
<div id="include">
	&copy; 2020 Gōngxíngzhī
</div>
<!--...-->

The differences among the three are as follows:

  • th:insert: Directly inserts the template fragment;
  • th:replace: Directly replaces the current fragment;
  • th:include: Directly inserts the content of the specified fragment into the current fragment.

Fragment Expression Syntax#

The template primarily uses fragment expressions, with the syntax as follows:

  • ~{templatename::selector}: Introduces the specified template's specified fragment name;
  • ~{templatename}: Introduces all fragments of the specified template;
  • ~{:: selector}: Same as ~{this:: selector}, introduces the specified name of the current template's fragment.

Here, templatename refers to the template name, such as footer in the above text, and selector refers to the fragment name, such as copy in the above text.

Additionally, selector can also be an ID selector, class selector, or tag, allowing the use of related template fragments without defining th:fragment, as shown below:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Footer</title>
</head>
<body>
    <div id="head">
        <p>Using fragment expressions without defining th:fragment -- id</p>
    </div>
    <div class="head">
        <p>Using fragment expressions without defining th:fragment -- class</p>
    </div>
    <div>
        <span>Using fragment expressions without defining th:fragment -- span</span>
    </div>
</body>
</html>

The corresponding code fragments can be used in another template as follows:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Template Layout.</title>
</head>
<body>
    <!--Using fragment expressions without defining th:fragment-->
    <div th:insert="~{footer::#head}"></div>
    <div th:insert="~{footer::.head}"></div>
    <div th:insert="~{footer::span}"></div>
</body>
</html>

Run the project, and the result is as follows:

Using fragment expressions without defining th:fragment -- id
Using fragment expressions without defining th:fragment -- class
Using fragment expressions without defining th:fragment -- span

Parameterized Template Fragments#

When defining template fragments using th:fragment, parameters can be added as follows:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Footer</title>
</head>
<body>
    <!--Add parameters to the template fragment-->
    <div th:fragment="frag(name)" th:assert="${!#strings.isEmpty(name)}">
        <p th:text="'Public account name:' + ${name}">Default</p>
    </div>
</body>
</html>

Then, in the corresponding page, the above fragment can be referenced to pass the corresponding parameters as follows:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Template Layout.</title>
</head>
<body>
    <!--Parameterized template fragment-->
    <div th:insert="~{footer::frag(${gzh})}"></div>
    <!--This syntax allows multiple parameters, and the order can vary-->
    <div th:insert="~{footer::frag(name=${gzh})}"></div>
</body>
</html>

In the above code, the parameter value gzh=Gōngxíngzhī, and the result after running the project is as follows:

Public account name: Gōngxíngzhī
Public account name: Gōngxíngzhī

In the template fragment, the th:assert attribute can be used for parameter validation, meaning that only if the values of all expressions in th:assert are true will execution continue; otherwise, an exception will be thrown.

Removing Template Fragments#

To remove template fragments, the th:remove attribute is used, with the following possible values:

  • all: Remove the current tag and all its child tags;
  • body: Do not remove the current tag, only remove the corresponding child tags;
  • tag: Only remove the current tag, not delete its child tags;
  • all-but-first: Remove all child tags except the first one;
  • none: No removal operation.

The specific usage is as follows:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<table>
    <tr>
        <th>NAME</th>
        <th>PRICE</th>
        <th>COMMENT</th>
    </tr>
    <!--Remove the current tag and all its child tags-->
    <tr th:remove="all">
        <td>A</td>
        <td>1</td>
        <td>AA</td>
    </tr>
    <!--Do not remove the current tag, only remove the corresponding child tags-->
    <tr th:remove="body">
        <td>B</td>
        <td>2</td>
        <td>BB</td>
    </tr>
    <!--Only remove the current tag, not delete its child tags-->
    <tr th:remove="tag">
        <td>C</td>
        <td>3</td>
        <td>CC</td>
    </tr>
    <!--Remove all child tags except the first one-->
    <tr th:remove="all-but-first">
        <td>D</td>
        <td>4</td>
        <td>DD</td>
    </tr>
    <!--No removal operation-->
    <tr th:remove="none">
        <td>E</td>
        <td>5</td>
        <td>EE</td>
    </tr>
</table>
</body>
</html>

Focus mainly on the effects after running with different values set for the th:remove attribute, which results in a page like the following:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<table>
    <tr>
        <th>NAME</th>
        <th>PRICE</th>
        <th>COMMENT</th>
    </tr>
    <!--Remove the current tag and all its child tags-->
    
    <!--Do not remove the current tag, only remove the corresponding child tags-->
    <tr></tr>
    <!--Only remove the current tag, not delete its child tags-->
        <td>C</td>
        <td>3</td>
        <td>CC</td>
    <!--Remove all child tags except the first one-->
    <tr>
        <td>D</td>
    </tr>
    <!--No removal operation-->
    <tr>
        <td>E</td>
        <td>5</td>
        <td>EE</td>
    </tr>
</table>
</body>
</html>

Template Layout Inheritance#

Template layout inheritance still uses th:fragment and th:replace. Below is an example demonstrating the writing of template layout inheritance, defining the page to be inherited as follows:

<!DOCTYPE html>
<html th:fragment="layout (title, content)" xmlns:th="http://www.thymeleaf.org">
<head>
    <title th:replace="${title}">Layout Title</title>
</head>
<body>
<h1>Layout H1</h1>
<div th:replace="${content}">
    <p>Layout content</p>
</div>
<footer>
    Layout footer
</footer>
</body>
</html>

The file inheriting the above page will replace the values of title and content. The writing of the inheriting page is as follows:

<!DOCTYPE html>
<html th:replace="~{base :: layout(~{::title}, ~{::section})}" xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Page Title</title>
</head>
<body>
<section>
    <p>Page content</p>
    <div>Included on page</div>
</section>
</body>
</html>

The result after running is as follows:

Layout H1

Page content

Included on page
Layout footer
Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.