FreeMarker

link ref

assign

  • ref

  • Simple variable assignment

<#assign name = "John">
Hello, ${name}! Welcome to FreeMarker.

<#assign user = "HuyNa">
<#assign x="Hello ${user}!">

<#assign firstName = "Alice" lastName = "Smith" age = 25>
List: ${firstName}, ${lastName}, ${age}


<#-- Multi-line string -->
<#assign message>
    Welcome to FreeMarker!
    This is a multi-line message.
</#assign>

Message:
${message}

  • Number assignment and arithmetic
<#assign x = 5>
<#assign y = 10>
Sum: ${x + y}

  • Update variable
<#-- Update variable using += -->
<#assign x += 5>
Updated value: ${x}

<#-- String concatenation -->
<#assign str = "Hello">
<#assign str = str + ", world!">
Updated string: ${str}

  • Boolean variable with string formatting
<#assign isAdult = true>
Is Adult: ${isAdult?string("Yes", "No")}

  • List (Array)
<#assign numbers = [1, 2, 3, 4, 5]>
Numbers: ${numbers?join(", ")}

<#assign myArray = ["apple", "banana", "cherry"]>
myArray: ${myArray?join(", ")}
  • Map (Hash)
<#assign map = {"name": "John", "age": 25}>
Map:
<#list map as key, value>
    ${key} = ${value}
</#list>

  • Assign variables inside the namespace
<#import "car.ftl" as userInfo>

<#assign userName = "John" in userInfo>
<#assign userAge = 30 in userInfo>

User's name: ${userInfo.userName}
User's age: ${userInfo.userAge}

attempt, recover

Như try-catch

Primary content
<#attempt>
  Optional content: ${thisMayFail}
<#recover>
  Ops! The optional content is not available.
</#attempt>
Primary content continued

autoesc

Escape là quá trình chuyển đổi các ký tự đặc biệt (&, <, >, ", ') thành mã an toàn để tránh lỗi hoặc tấn công XSS.

<#ftl output_format="XML" auto_esc=false>
${"&"}
<#autoesc>
  ${"&"}
  ...
  ${"&"}
</#autoesc>
${"&"}

Nếu không escape thì có thể hiểu tương tự như này:

<input type="text" value="<script>alert('Hacked!')</script>">

  • Nếu một biến chứa <script>, nó sẽ được thoát thành &lt;script&gt;, tránh lỗi bảo mật.

compress

  • ref

  • Tất cả khoảng trắng liên tiếp sẽ bị nén lại thành 1 khoảng trắng duy nhất.

  • Nếu khoảng trắng chứa dòng mới, nó sẽ thay bằng một dòng mới duy nhất.

  • Khoảng trắng đầu tiên và cuối cùng bị loại bỏ hoàn toàn.

<#compress>
    Nội dung có   nhiều    khoảng trắng
    và dòng mới

    sẽ được nén lại.

</#compress>

flush

  • ref

  • Gửi nội dung từng phần: Khi trang web cần hiển thị dữ liệu từng phần trước thay vì chờ tải toàn bộ.

  • Cập nhật giao diện nhanh hơn: Trình duyệt có thể hiển thị nội dung sớm trong khi dữ liệu tiếp tục tải.

  • Gửi phản hồi nhanh: Trong API hoặc xử lý dữ liệu lớn, giúp giảm thời gian chờ của người dùng.

<html>
<head><title>Trang của tôi</title></head>
<body>
    <h1>Chào mừng, ${user}!</h1>
    <#flush> <!-- Gửi phần đầu trang trước -->

    <p>Đang tải dữ liệu...</p>
    <#list items as item>
        <p>${item}</p>
    </#list>
</body>
</html>

ftl

  • ref

  • Là một chỉ thị đặc biệt giúp khai báo thông tin về template và cài đặt một số tùy chọn quan trọng, như:

    • Encoding (bộ mã hóa ký tự)
    • Tự động loại bỏ khoảng trắng
    • Chế độ tự động thoát (auto-escaping)
    • Định dạng đầu ra
    • Quy tắc xử lý XML
  • Chỉ thị #ftl phải luôn đứng đầu file template (không có khoảng trắng hay ký tự nào phía trước). Nếu có, FreeMarker sẽ bỏ qua chúng.

# File sẽ được đọc và xử lý với UTF-8, ngay cả khi hệ thống đang dùng ISO-8859-1.
<#ftl encoding="UTF-8">

# Tự động xóa khoảng trắng không cần thiết trong toàn bộ template.
<#ftl strip_whitespace=true>

# Mọi văn bản không nằm trong <#macro> hoặc biến ${} sẽ bị xóa.
<#ftl strip_text=true>

# Bảo vệ template khỏi lỗi XSS khi xử lý dữ liệu HTML.
<#ftl auto_esc=true>

# Định nghĩa kiểu nội dung của template (HTML, XML, JSON, PlainText).
<#ftl output_format="HTML">

# Định nghĩa prefix để ánh xạ vào namespace XML. Khi làm việc với dữ liệu XML có namespace, cần đặt prefix để truy vấn chính xác.
<#ftl ns_prefixes={"e": "http://example.com/ebook"}>

# Template sẽ không chấp nhận cú pháp FreeMarker cũ.
<#ftl strict_syntax=true>

Tham sốMô tả
encodingĐặt bộ mã hóa cho template (ví dụ: UTF-8).
strip_whitespaceLoại bỏ khoảng trắng dư thừa (true/false).
strip_textXóa văn bản cấp cao nhất (true/false).
auto_escBật/tắt tự động thoát ký tự (true/false).
output_formatXác định định dạng đầu ra (HTML, XML, JSON, v.v.).
ns_prefixesXác định prefix cho namespace XML.
strict_syntaxBật/tắt cú pháp chặt chẽ (true/false).

function, return

  • ref

  • Bắt buộc phải có #return (nếu không sẽ undefined).

  • Không thể in ra như #macro.

<#function function_name param1 param2 ...>
    ...
    <#return value>
</#function>

Example:

<#function avg x y>
    <#return (x + y) / 2>
</#function>

Trung bình của 10 và 20 là: ${avg(10, 20)}

<!-- ---------------------------------------- -->
<#function avg nums...>
    <#local sum = 0>
    <#list nums as num>
        <#local sum += num>
    </#list>
    <#if nums?size != 0>
        <#return sum / nums?size>
    </#if>
</#function>

Trung bình của 10 và 20 là: ${avg(10, 20)}
Trung bình của 10, 20, 30, 40 là: ${avg(10, 20, 30, 40)}
Trung bình của danh sách rỗng: ${avg()!"N/A"}

<!-- ---------------------------------------- -->
<#function isEven n>
    <#return (n % 2 == 0)>
</#function>

Số 10 là số chẵn? ${isEven(10)?string("Có", "Không")}
Số 15 là số chẵn? ${isEven(15)?string("Có", "Không")}

<!-- ---------------------------------------- -->
<#function containsKeyword text keyword>
    <#return text?contains(keyword)>
</#function>

Chuỗi "Hello FreeMarker" có chứa "FreeMarker" không? ${containsKeyword("Hello FreeMarker", "FreeMarker")?string("Có", "Không")}

global

  • ref

  • Nếu một biến cục bộ có cùng tên với biến global, biến cục bộ sẽ ưu tiên hơn.

  • Để truy cập biến global khi có xung đột tên, dùng ${.globals.varName}.

<#global siteName = "FreeMarker Guide">
<#global version = 2.3>

Tên website: ${siteName}
Phiên bản: ${version}

<!-- -------------------------------------- -->
<#global message = "Hello from Global">
<#assign message = "Hello from Local">

Cục bộ: ${message}
Toàn cục: ${.globals.message}

<!-- -------------------------------------- -->
<#global content>
    Nội dung toàn cục này có thể chứa nhiều dòng.
</#global>

Nội dung: ${content}

<!-- -------------------------------------- -->
<#global appName = "MyApp" version = "1.0.0" author = "Gia Huy">

Ứng dụng: ${appName}
Phiên bản: ${version}
Tác giả: ${author}

<!-- -------------------------------------- -->
<!-- dùng biến global từ template khác -->
<#import "config.ftl" as cfg>

Tên website: ${.globals.siteName}
Email admin: ${.globals.adminEmail}

if, else, elseif

<#assign x = 5>

<#if x == 1>
    x là 1
<#elseif x == 2>
    x là 2
<#elseif x == 3>
    x là 3
<#elseif x == 4>
    x là 4
<#else>
    x không phải 1, 2, 3 hoặc 4
</#if>

Tính năngMô tả
Điều kiện cơ bản<#if x == 1>, <#if x gt 10>
Nhiều điều kiện (#elseif)Dùng để kiểm tra thêm điều kiện khác
Mặc định (#else)Chạy khi không có điều kiện nào đúng
Toán tử so sánhgt, gte, lt, lte, ==, !=
Kiểm tra chuỗi rỗng?has_content
Lồng nhau (nested if)Có thể lồng nhiều #if bên trong

import

  • ref

  • Dùng để nạp một file template vào một namespace riêng, giúp sử dụng biến, macro, function được khai báo trong file đó.

<#macro hello name="User">
    Chào ${name}!
</#macro>
<!-- ---- -->
<#import "macros.ftl" as myMacros>

<@myMacros.hello name="Gia Huy" />
<@myMacros.hello />

<!-- ------------------------------------ -->
<#global siteName = "FreeMarker Site">
<#global adminEmail = "admin@example.com">
<!-- ---- -->
<#import "config.ftl" as cfg>

Tên website: ${cfg.siteName}
Email admin: ${cfg.adminEmail}

<!-- ------------------------------------ -->
<#function square x>
    <#return x * x>
</#function>
<!-- ---- -->
<#import "functions.ftl" as func>

Bình phương của 5 là: ${func.square(5)}

include

<#include "file.ftl">

<#include "file.ftl" parse=true encoding="UTF-8" ignore_missing=true>

  • #include file chứa nội dung HTML
<hr>
<p>Bản quyền 2024 - Gia Huy</p>
<!-- ------ -->
<h1>Trang chính</h1>
<p>Chào mừng đến với trang chủ.</p>

<#include "footer.ftl">

  • #include với biến chia sẻ
Bản quyền 2024 ${owner}
<!-- --------- -->
<#assign owner = "Công ty ABC">

<h1>Giới thiệu</h1>
<#include "copyright.ftl">

list, else, items, sep, break, continue

list

<#assign users = ["Alice", "Bob", "Charlie"]>

<#list users as user>
    ${user}
</#list>

<!-- ---------------------------------------- -->

<#assign products = { "Apple": 5, "Banana": 10, "Cherry": 15 }>

<#list products as name, price>
    ${name}: ${price}
</#list>

else

  • Nếu danh sách trống, có thể dùng #else để hiển thị nội dung mặc định.
<#assign users = []>

<#list users as user>
    ${user}
<#else>
    Không có người dùng.
</#list>

items

  • Dùng để tách phần trước và sau danh sách khỏi vòng lặp.
  • Không dùng items: có thể tạo ra list rỗng

<#assign users = []>

<ul>
    <#list users as user>
        <li>${user}</li>
    </#list>
</ul>

  • Dùng items:
<#assign users = ["Alice", "Bob", "Charlie"]>

<#list users>
    <ul>
        <#items as user>
            <li>${user}</li>
        </#items>
    </ul>
<#else>
    <p>Không có người dùng.</p>
</#list>

sep

  • Thêm dấu phân tách giữa các phần tử
<#assign users = ["Alice", "Bob", "Charlie"]>

<#list users as user>
    ${user}<#sep>, </#sep>
</#list>

<!-- Alice, Bob, Charlie -->

break

<#list 1..5 as num>
    ${num}
    <#if num == 3>
        <#break>
    </#if>
</#list>

continue

<#list 1..5 as num>
    <#if num == 3>
        <#continue>
    </#if>
    ${num}
</#list>

local

  • ref

  • Dùng để khai báo biến cục bộ (local variable).

  • Chỉ hoạt động bên trong macro hoặc function.

<#macro demoAssign>
    <#assign globalVar = "Tôi là biến toàn cục">
</#macro>

<@demoAssign />
Biến bên ngoài macro: ${globalVar}


<#macro demoLocal>
    <#local localVar = "Tôi là biến cục bộ">
</#macro>

<@demoLocal />
Biến bên ngoài macro: ${localVar} <!-- Lỗi -->

macro, nested, return

macro

  • ref

  • Tương tự như hàm (function) trong lập trình, nhưng nó không trả về giá trị mà chỉ tạo ra một đoạn nội dung có thể tái sử dụng trong template.

<#macro hello>
    Xin chào FreeMarker!
</#macro>

<@hello/>

<!-- ---------------------------- -->

<#macro greet name>
    Xin chào, ${name}!
</#macro>

<@greet name="Gia Huy"/>

<!-- ---------------------------- -->

<#macro greet name="bạn">
    Xin chào, ${name}!
</#macro>

<@greet/>
<@greet name="Huy"/>

nested

  • Dùng để gọi lại nội dung được truyền vào macro.
<#macro ten_macro>
    Nội dung trước
    <#nested>
    Nội dung sau
</#macro>

<@ten_macro>Đây là nội dung nested</@ten_macro>

<!-- ---------------- -->
Nội dung trước
Đây là nội dung nested
Nội dung sau

``ftl <#macro repeat> 1. <#nested> 2. <#nested> </#macro>

@repeatXin chào FreeMarker!/@repeat


```ftl
<#macro displayProducts products>
    <#list products as item>
        <#nested item.name, item.price>
    </#list>
</#macro>

<@displayProducts products=[{"name":"Laptop", "price":1000}, {"name":"Phone", "price":500}]; pName, pPrice>
    Sản phẩm: ${pName} - Giá: $${pPrice}
</@displayProducts>

return

  • Dừng macro ngay lập tức.
  • Bỏ qua tất cả nội dung sau #return.
<#macro checkAge age>
    <#if age < 18>
        Bạn chưa đủ tuổi.
        <#return>
    </#if>
    Chào mừng bạn!
</#macro>

<@checkAge age=16/>
<@checkAge age=20/>

noautoesc

  • ref

  • Để tắt auto-escaping cho một phần cụ thể trong template.

<#ftl output_format="XML">
${"&"}
<#noautoesc>
  ${"&"}
</#noautoesc>
${"&"}

noparse

  • ref

  • Giúp hiển thị nội dung template nguyên bản mà không để FreeMarker xử lý nó.

<#noparse>
  <#list animals as animal>
  <tr><td>${animal.name}<td>${animal.price} Euros
  </#list>
</#noparse>

nt

outputformat

<#ftl output_format="XML">
XML escaping: ${"&{}"}

<#outputformat "RTF">
  RTF escaping: ${"&{}"}
</#outputformat>

<#outputformat "plainText">
  No escaping: ${"&{}"}
</#outputformat>

XML escaping again: ${"&{}"}

setting

<#-- Thiết lập ngôn ngữ và múi giờ -->
<#setting locale="en_US">
<#setting time_zone="Asia/Ho_Chi_Minh">

<#-- Thiết lập định dạng số -->
<#setting number_format="0.00">

<#-- Thiết lập định dạng Boolean -->
<#setting boolean_format="Yes,No">

<#-- Thiết lập định dạng ngày tháng -->
<#setting date_format="dd/MM/yyyy">
<#setting time_format="HH:mm:ss">
<#setting datetime_format="dd/MM/yyyy HH:mm:ss">

<#-- Gán giá trị để hiển thị -->
<#assign number = 12345.6789>
<#assign isAdmin = true>
<#assign today = .now>


- Ngôn ngữ: ${.locale}
- Múi giờ: ${.time_zone}


- Số gốc: 12345.6789
- Số sau khi format: ${number}


- Trạng thái Admin: ${isAdmin}

- Ngày: ${today?date}
- Giờ: ${today?time}
- Ngày & Giờ: ${today?datetime}

stop

switch, on, case, default, break

<#assign size = "large">

<#switch size>
  <#on "small">
    Kích thước: Nhỏ
  <#on "medium">
    Kích thước: Vừa
  <#on "large", "extra large">
    Kích thước: Lớn
    <#break>
  <#default>
    Kích thước: Không xác định
</#switch>

t, lt, rt

  • ref

  • <#t> (trim): Xóa tất cả khoảng trắng ở đầu và cuối dòng chứa nó.

  • <#lt> (left trim): Xóa khoảng trắng đầu dòng.

  • <#rt> (right trim): Xóa khoảng trắng cuối dòng.

User-defined directive (<@...>)

visit, recurse, fallback