Page fragment trong EzyArticle là một “mảnh giao diện” có thể tái sử dụng giữa nhiều trang. Thay vì lặp lại HTML, CSS nhỏ và script nhỏ ở nhiều nơi, ta tách chúng thành các fragment rồi ghép lại khi render trang.

Page fragment là gì?

Một page fragment thường gồm 3 phần độc lập:
  • content.html: phần HTML chính hiển thị trong thân trang
  • head.html: phần được chèn vào bên trong thẻ <head>
  • foot.html: phần được chèn vào cuối <body>
Cách tổ chức này giúp một fragment không chỉ có giao diện, mà còn có thể mang theo phần <meta>, <link>, <style> hoặc script cần thiết cho chính nó.
flowchart TD
    A[Page Fragment] --> B[content.html]
    A --> C[head.html]
    A --> D[foot.html]

    B --> E[Nội dung hiển thị]
    C --> F[Meta, style, link, title]
    D --> G[Script hoac HTML cuối trang]

Cấu trúc thư mục

Ví dụ một fragment:
page-fragments/
  common/
    header/
      content.html
      head.html
      foot.html
      meta.json
Trong đó:
  • commonpage_name
  • headerfragment_name
Ví dụ meta.json:
{
  "title": "Common header fragment",
  "contentType": "HTML",
  "status": "DRAFT"
}

Nguyên lý hoạt động

Trong runtime, hệ thống nạp fragment theo cặp:
  • page_name
  • fragment_name
Sau đó ánh xạ 3 file thành 3 vùng dữ liệu:
  • content.html -> content
  • head.html -> additionalHead
  • foot.html -> additionalFoot
Khi view được render, các đoạn này không được in ra như text thô, mà được đưa qua template engine thêm một lần nữa. Nghĩa là nếu bên trong fragment có Thymeleaf, biểu thức đó vẫn tiếp tục được xử lý để tạo HTML cuối cùng.
sequenceDiagram
    participant P as Page
    participant T as Ezy block
    participant F as Fragment store
    participant R as Template engine

    P->>T: ezy:replace hoặc ezy:utext
    T->>F: Lay fragment theo page_name/fragment_name
    F-->>T: content/head/foot
    T->>R: Render lại nội dung fragment
    R-->>P: HTML cuối cùng
Nói ngắn gọn: page fragment là “template lồng trong template”.

Hai cách dùng chính

1. Chèn trực tiếp bằng ezy:replace

Dùng khi bạn muốn gọi thẳng một fragment từ trong page hoặc từ fragment khác.
Cú pháp:
<ezy:block ezy:replace="~{page_name/fragment_name :: region_name}" />
Ví dụ:
<ezy:block ezy:replace="~{search/active :: content}" />
<ezy:block ezy:replace="~{search/active :: head}" />
<ezy:block ezy:replace="~{search/active :: foot}" />
Ý nghĩa:
  • searchpage_name
  • activefragment_name
  • content, head, foot là vùng cần lấy
Quy tắc quan trọng:
  • chỉ chèn :: content vào content.html của page
  • chỉ chèn :: head vào head.html
  • chỉ chèn :: foot vào foot.html
Ví dụ đúng với trang home:
pages/home/content.html
<ezy:block ezy:replace="~{search/active :: content}" />
pages/home/head.html
<ezy:block ezy:replace="~{search/active :: head}" />
pages/home/foot.html
<ezy:block ezy:replace="~{search/active :: foot}" />

2. Render từ biến pageFragments bằng ezy:utext

Dùng khi backend đã nạp sẵn fragment map cho view.
Ví dụ:
<ezy:block ezy:utext="" />
<ezy:block ezy:utext="" />
<ezy:block ezy:utext="" />
Điểm hay ở đây là giá trị lấy ra vẫn được render tiếp như Thymeleaf/HTML, nên fragment có thể chứa biến, điều kiện, lời gọi hàm backend an toàn do hệ thống cho phép.

Ví dụ hoàn chỉnh

Giả sử bạn muốn tạo fragment post_details/content.
page-fragments/post_details/content/content.html
<section class="post-detail">
  <h1>Chi tiet bai viet</h1>
</section>
page-fragments/post_details/content/head.html
<style>
  .post-detail {
    padding: 24px;
  }
</style>
page-fragments/post_details/content/foot.html
<script>
  console.log('post detail fragment loaded');
</script>
Sau đó nhúng vào trang:
pages/home/content.html
<ezy:block ezy:replace="~{post_details/content :: content}" />
pages/home/head.html
<ezy:block ezy:replace="~{post_details/content :: head}" />
pages/home/foot.html
<ezy:block ezy:replace="~{post_details/content :: foot}" />

Fragment có nhận tham số không?

Có. Runtime hỗ trợ truyền tham số theo kiểu:
<ezy:block ezy:replace="~{page_name/fragment_name :: content(title='Xin chao', slug=)}" />
Các tham số này sẽ được đưa vào context render của fragment. Phần này phù hợp khi bạn muốn tái sử dụng cùng một fragment nhưng thay đổi dữ liệu đầu vào.
Lưu ý thực tế từ code:
  • tham số theo dạng name=value
  • hỗ trợ chuỗi 'text'
  • hỗ trợ expression như

Khi nào nên dùng page fragment?

Nên dùng khi:
  • header, footer, banner, khối CTA được dùng lại ở nhiều trang
  • một phần giao diện có cả HTML, head và foot đi kèm
  • muốn tách giao diện thành các khối nhỏ để dễ bảo trì
Không nên lạm dụng khi:
  • fragment quá nhỏ, chỉ 1 dòng đơn giản
  • lồng fragment quá sâu làm khó debug
  • đưa script lớn vào head.html

Một số khuyến nghị thực hành tốt

  • Đặt tên rõ nghĩa như common/header, common/footer, post_details/content
  • Giữ head.html cho meta, style, link; hạn chế logic JS nặng
  • Giữ foot.html cho script hoặc HTML cuối trang
  • Cùng một fragment thì luôn ghép đúng vùng content/head/foot
  • Nếu cập nhật media tĩnh, nên thêm query version như ?v=timestamp để tránh cache cũ

Quy trình sử dụng thường gặp

  1. Tạo thư mục fragment trong page-fragments
  2. Tạo content.html, head.html, foot.html, meta.json
  3. Đồng bộ fragment về hoặc lên server bằng extension
  4. Nhúng fragment vào page bằng ezy:replace hoặc render qua pageFragments
  5. Preview
  6. Publish

Kết luận

Page fragment trong EzyArticle là cơ chế ghép trang theo module: mỗi fragment là một đơn vị giao diện có thể mang theo cả phần hiển thị, phần <head> và phần cuối trang. Nhờ đó, hệ thống vừa tái sử dụng được UI, vừa giữ được khả năng render động bằng Thymeleaf.