# 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.

```xml
<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.

```xml
<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.

```xml
<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.

```xml
<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.

```xml
<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.

```xml
<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.

```xml
<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. **Fragments can be nested**. That is, one fragment can contain another fragment. However, Make sure you don’t make a cyclic dependency. For example, the following scenario will lead to infinite recursion.![](data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjEwOSIgd2lkdGg9IjQ5NCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2ZXJzaW9uPSIxLjEiLz4=)![](https://springhow.com/wp-content/uploads/2021/02/Cyclic-Dependency-in-Fragments.png)
2. In both `th:insert` and `th:replace` the content of the surrounding div is removed.
3. Fragments are loaded from the relative path from the file it is invoked.
4. 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 **Fragmen**t 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 đó:

![](https://s1.o7planning.com/vi/12371/images/40150192.png)

```

~{/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:

![](https://s1.o7planning.com/vi/12371/images/40176905.png)

![](https://s1.o7planning.com/vi/12371/images/40177373.png)

![](https://s1.o7planning.com/vi/12371/images/40175780.png)

![](https://s1.o7planning.com/vi/12371/images/40175635.png)

![](https://s1.o7planning.com/vi/12371/images/40175318.png)

![](https://s1.o7planning.com/vi/12371/images/40175145.png)

```

~{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

![](https://s1.o7planning.com/vi/12371/images/40181087.png)

```

<!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>
```
