openplanning

Sử dụng Fragment trong Thymeleaf

  1. Fragment là gì?
  2. th:insert, th:replace, th:include
  3. Fragment với các tham số
  4. th:assert

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 minh
Bạ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>