FreeMarker
- assign
- attempt, recover
- autoesc
- compress
- flush
- ftl
- function, return
- global
- if, else, elseif
- import
- include
- list, else, items, sep, break, continue
- local
- macro, nested, return
- noautoesc
- noparse
- nt
- outputformat
- setting
- stop
- switch, on, case, default, break
- t, lt, rt
- User-defined directive (<@...>)
- visit, recurse, fallback
assign
-
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 <script>, tránh lỗi bảo mật.
compress
-
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
-
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
-
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_whitespace | Loại bỏ khoảng trắng dư thừa (true/false). |
strip_text | Xóa văn bản cấp cao nhất (true/false). |
auto_esc | Bật/tắt tự động thoát ký tự (true/false). |
output_format | Xác định định dạng đầu ra (HTML, XML, JSON, v.v.). |
ns_prefixes | Xác định prefix cho namespace XML. |
strict_syntax | Bật/tắt cú pháp chặt chẽ (true/false). |
function, return
-
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
-
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ăng | Mô 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ánh | gt, 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
-
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
-
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
-
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
-
Để tắt auto-escaping cho một phần cụ thể trong template.
<#ftl output_format="XML">
${"&"}
<#noautoesc>
${"&"}
</#noautoesc>
${"&"}
noparse
-
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
-
<#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.