Fragment Thymeleaf

https://springhow.com/thymeleaf-fragments/

Thymeleaf fragments let you break down your templates into reusable layout elements. For example, Several pages may have different content but still have the same header and footer. In these cases, Writing these into a separate template file and reusing it is not a bad idea.

Fragments in Action

Let’s take this snippet from a hypothetical home page.

<body class="container">
<nav class="navbar bg-dark navbar-dark navbar-expand-lg">
    <a class="navbar-brand" href="#">Logo</a>
    <div class="navbar-nav w-100 ml-auto" th:each="menu : ${menus}">
        <a class="nav-link" th:href="${menu.url}" th:text="${menu.label}"></a>
    </div>
</nav>
....
.... Some content of the home page
....
</body>Code language: HTML, XML (xml)

Here, The navbar logic would never change for different thymeleaf view. In these cases, we can define the navbar HTML into a separate fragment and import it everywhere as we want it. Let’s split our template.

First, create a thymeleaf fragment file fragments/navbar.html under the templates directory.

<nav class="navbar bg-dark navbar-dark navbar-expand-lg">
    <a class="navbar-brand" href="#">Logo</a>
    <div class="navbar-nav w-100 ml-auto" th:each="menu : ${menus}">
        <a class="nav-link" th:href="${menu.url}" th:text="${menu.label}"></a>
    </div>
</nav>Code language: HTML, XML (xml)

Now, you can include this fragment into the home.html as shown below.

<body class="container">
<div th:replace="fragments/navbar"></div>
....
.... Some content of the home page
....
</body>Code language: HTML, XML (xml)

Ways to include thymeleaf fragments

We can include other fragments into a template in two different ways. Let’s find about each one of them.

th:insert Directive

If you want the fragment to be wrapped around by a parent tag, you should use th:insert. Here is the syntax for this approach.

<div th:insert="fragments/navbar" id="this-will-be-retained">This content will be gone</div>Code language: HTML, XML (xml)

The above snippet will result in a div containing the content of the navbar.html as shown below.

<div id="this-will-be-retained">
    <nav class="navbar bg-dark navbar-dark navbar-expand-lg">
        <a class="navbar-brand" href="#">Logo</a>
        <div class="navbar-nav">
            <a class="nav-link" href="/">Home</a>
        </div>
        <div class="navbar-nav">
            <a class="nav-link" href="/about">About us</a>
        </div>
        <div class="navbar-nav">
            <a class="nav-link" href="/contact">Contact us</a>
        </div>
    </nav>
</div>Code language: HTML, XML (xml)

As you see, the div tag exists with its original attribute.

th:replace directive

You can specify where to include a fragment using the th:replace directive. This directive will remove the current element and fill it with the content of the fragment.

<div th:replace="fragments/navbar">This content will be gone</div>Code language: HTML, XML (xml)

Here the <div> element itself will be replaced by the content of navbar.html. The resultant code would look like this.

<nav class="navbar bg-dark navbar-dark navbar-expand-lg">
    <a class="navbar-brand" href="#">Logo</a>
    <div class="navbar-nav">
        <a class="nav-link" href="/">Home</a>
    </div>
    <div class="navbar-nav">
        <a class="nav-link" href="/about">About us</a>
    </div>
    <div class="navbar-nav">
        <a class="nav-link" href="/contact">Contact us</a>
    </div>
</nav>Code language: HTML, XML (xml)

Points to note about thymeleaf fragments

  1. In both th:insert and th:replace the content of the surrounding div is removed.

  2. Fragments are loaded from the relative path from the file it is invoked.

  3. There is also a th:include But this is just a deprecated version of th:insert.

1- Fragment là gì?

Một Fragment (Mảnh) là một phần trong một Template. Thymeleaf cho phép bạn nhập khẩu các fragment của Template này vào một Template khác. Có rất nhiều cách để bạn xác định một Fragment. Chẳng hạn:

  • Lựa chọn tất cả các thẻ (tag) có thuộc tính (attribute) th:fragment="search-fragment".

  • Lựa chọn thẻ theo ID.

  • Lựa chọn tất cả các thẻ theo Css-class.

  • ....

Điều quan trọng khi bạn muốn nhập khẩu một Fragment của một Template nào đó, bạn phải mô tả được vị trí của nó.Cú pháp để mô tả vị trí của một Fragment thuộc một Template nào đó:


~{/path-to-template/template-name :: selector}

Nếu bạn muốn mô tả vị trí của Fragment thuộc Template hiện tại, thì có thể sử dụng một cú pháp ngắn gọn hơn:


~{:: selector}

~{this :: selector}

~{templatename: fragmentname} (th:fragment)Lựa chọn fragment theo tên.~{templatename: tagname} (Tag Name)Lựa chọn fragment theo tên của thẻ.Lựa chọn các thẻ con của một thẻ. Chẳng hạn, lựa chọn tất cả các thẻ <script> nằm trong thẻ <header>:~{templatename: #id} (ID)Lựa chọn fragment theo giá trị của thuộc tính (attribute) ID của thẻ.~{templatename: .classname},~{templatename: tagname.classname} (Css Class)Lựa chọn fragment theo Css Class:~{templatename} (Everything)Lựa chọn tất cả trong Template:Other...Các ví dụ khác:


~{fragments/my-template :: #tagId/text() }

~{fragments/my-template :: fragmentName/text() }

~{fragments/my-template :: tagName/text() }

2- th:insert, th:replace, th:include

Tính năng cho phép nhập khẩu các fragment từ một Template này vào một Template khác thực sự rất tuyệt vời, nó giúp cho việc thiết kế giao diện của website trở lên dễ dàng hơn. Hãy xem một ví dụ dưới đây:Trong ví dụ này, tập tin my-template.html chứa nhiều fragment, mà các Template khác có thể nhập khẩu để sử dụng./fragments/my-template.html


<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8" />
    <title>My Template</title>
    <link rel="stylesheet" type="text/css" th:href="@{/main.css}"/>
    <link rel="stylesheet" type="text/css" th:href="@{/secondary.css}"/>
    <script type="text/javascript" th:src="@{/main.js}"></script>
    <script type="text/javascript" th:src="@{/secondary.js}"></script>
</head>
<body>
    <!-- Script in body -->
    <script type="text/javascript" th:src="@{/script-in-body.js}"></script>
    <ul th:fragment="my-fragment1">
        <li><a th:href="@{/}">Home</a></li>
        <li><a th:href="@{/products}">Products</a></li>
        <li><a th:href="@{/about}">About</a></li>
    </ul>
    <ul th:fragment="my-fragment2">
        <li><a th:href="@{/admin/products}">Product Management</a></li>
        <li><a th:href="@{/admin/orders}">Order Management</a></li>
    </ul>
    <div id = "my-id1">
         Element DIV with id = 'my-id1'
    </div>
    <aside>
        <div>This is a sidebar</div>
    </aside>
    <p class="my-class">Element P with class (my-class)</p>
    <p>Element P without class</p>
    <p class="my-class">Element P with class (my-class)</p>
</body>
</html>

Tập tin my-page.html là một Template, nó nhập khẩu một vài fragment của my-template.html:my-page.html (Template)


<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title th:replace = "~{fragments/my-template :: title}">Title</title>
<th:block th:insert="~{fragments/my-template :: link}"></th:block>
<th:block th:insert="~{fragments/my-template :: head/script}"></th:block>
</head>
<body>
   <h1>My Page</h1>
   <p>Some Content of My Page</p>
   <div th:insert="~{fragments/my-template :: my-fragment1}"></div>
   <div th:insert="~{fragments/my-template :: my-fragment2}"></div>
   <div th:insert="~{fragments/my-template :: #my-id1}"></div>
   <div th:insert="~{fragments/my-template :: p.my-class }"></div>
</body>
</html>

Kết quả mà bạn nhận được:(HTML Result)


<!DOCTYPE HTML>
<html>
<head>
    <meta charset="UTF-8" />
    <title>My Template</title>
    <link rel="stylesheet" type="text/css" href="/main.css" />
    <link rel="stylesheet" type="text/css" href="/secondary.css" />
    <script type="text/javascript" src="/main.js"></script>
    <script type="text/javascript" src="/secondary.js"></script>
</head>
<body>
    <h1>My Page</h1>
    <p>Some Content of My Page</p>
    <div>
        <ul>
            <li><a href="/">Home</a></li>
            <li><a href="/products">Products</a></li>
            <li><a href="/about">About</a></li>
        </ul>
    </div>
    <div>
        <ul>
            <li><a href="/admin/products">Product Management</a></li>
            <li><a href="/admin/orders">Order Management</a></li>
        </ul>
    </div>
    <div>
        <div id="my-id1">
            Element DIV with id = 'my-id1'
        </div>
    </div>
    <div>
        <p class="my-class">Element P with class (my-class)</p>
        <p class="my-class">Element P with class (my-class)</p>
    </div>
</body>
</html>

th:insert, th:replace, th:include

  • th:insert sẽ trèn Fragment vào thành con của thẻ mục tiêu (Target tag).

  • th:replace sẽ thay thế thẻ mục tiêu bởi Fragment.

  • th:include sẽ trèn con của Fragment vào thành con của thẻ mục tiêu.

Chú ý: Thuộc tính (attribute) th:include được sử dụng trong Thymeleaf 2, và không còn được sử dụng nữa trong phiên bản Thymeleaf 3.

/path/mytemplate.html (Fragments)


<footer th:fragment="copyright">
  &copy; o7planning.org
</footer>

Template mục tiêu:(Target Template)


<body>
  ...
  <!-- th:insert -->
  <div th:insert="~{/path/mytemplate :: copyright}"></div>
  <!-- th:replace -->
  <div th:replace="~{/path/mytemplate :: copyright}"></div>
  <!-- th:include -->
  <div th:include ="~{/path/mytemplate :: copyright}"></div>
</body>

Kết quả:(HTML Result)


<body>
  ...
  <!-- th:insert -->
  <div>
    <footer>
      &copy; o7planning.org
    </footer>
  </div>
  <!-- th:replace -->
  <footer>
    &copy; o7planning.org
  </footer>
  <!-- th:include -->
  <div>
    &copy; o7planning.org
  </div>
</body>

3- Fragment với các tham số

Một fragment sẽ giống như một hàm (function) hơn nếu nó có thêm các tham số, và thật may mắn Thymeleaf hỗ trợ điều này.Tham số tường minh:Nếu bạn khai báo một fragment và liệt kê một cách tường minh danh sách các tham số của nó. Các tham số này sẽ là các tham số bắt buộc. Chẳng hạn như ví dụ dưới đây:


<div th:fragment="person (firstName, lastName)" class="box">
    <p>First Name: <span th:utext="${firstName}"></span></p>
    <p>Last Name: <span th:utext="${lastName}"></span></p>
    <p>Full Name: <span th:utext="${firstName} + ' '+ ${lastName}"></span></p>
</div>

Truyền tham số khi bạn gọi fragment:


<div th:replace="~{fragments/my-template2 :: person('Donald', 'Trump') }"></div>
    
<div th:replace="~{fragments/my-template2 :: person( firstName='Donald', lastName= 'Trump') }"></div>

<div th:replace="~{fragments/my-template2 :: person( ${u.firstName}, ${u.lastName}) }"></div>

<div th:replace="~{fragments/my-template2 :: person( firstName=${u.firstName}, lastName= ${u.lastName}) }"></div>

Tham số không tường minhBạn có thể tạo ra một fragment với các tham số không tường minh, chúng là các tham số không bắt buộc, chẳng hạn ví dụ sau:


<!-- Fragment with implicit parameters. -->
<div th:fragment="greeting" class="box">
    <p>Hello
       <span th:utext="${title}"></span>
       <span th:utext="${name} ?: 'There'"></span>
    </p>
</div>

Gọi fragment có các tham số không tường minh.


<div th:replace="~{fragments/my-template2 :: greeting(title='Mr.', name = 'Tom') }"></div>    
    
<div th:replace="~{fragments/my-template2 :: greeting }"></div>  

4- th:assert

Thuộc tính (attribute) th:assert giúp bạn đánh giá một biểu thức, hoặc nhiều biểu thức (Các biểu thức cách nhau bởi dấu phẩy). Nếu tất cả các biểu thức được đánh giá là true (đúng) sẽ không có vấn đề gì xẩy ra, nếu có 1 biểu thức bị đánh giá là false (sai) một ngoại lệ sẽ được ném ra.


<div th:fragment="employee (firstName, lastName)" class="box"
     th:assert= "${!#strings.isEmpty(firstName)}, ${!#strings.isEmpty(lastName)}">
    
    <p>First Name: <span th:utext="${firstName}"></span></p>
    <p>Last Name: <span th:utext="${lastName}"></span></p>
    <p>Full Name: <span th:utext="${firstName} + ' '+ ${lastName}"></span></p>
</div>

Last updated