自動完成組件需要兩類數據:用戶選擇的值 (綁定到表單模型) 和 可選完成項列表 (靜態/動態數據源)。本文定義兩個 CDI Bean:`User` (會話級,存儲用戶選擇的國家) 和 `AutoComplete` (應用級,提供虛構國家列表),並解析其在 Facelet 中的綁定方式。
package com.corejsf;
import java.io.Serializable;
import javax.inject.Named;
import javax.enterprise.context.SessionScoped;
@Named
@SessionScoped
public class User implements Serializable {
private String country;
public String getCountry() { return country; }
public void setCountry(String country) { this.country = country; }
}
package com.corejsf;
import java.io.Serializable;
import javax.inject.Named;
import javax.enterprise.context.ApplicationScoped;
@Named
@ApplicationScoped
public class AutoComplete implements Serializable {
public String[] getCountries() {
return new String[] {
"Abari", "Absurdsvanj", "Adjikistan", "AfroMacoland",
"Agrabah", "Agaria", "Aijina", "Ajir", "Al-Alemand",
"Al Amarja", "Alaine", "Albenistan", "Aldestan",
"Al Hari", "Alpine Emirates", "Altruria",
"Allied States of America", "BabaKiueria", "Babalstan",
"Babar's Kingdom", "Backhairistan", "Bacteria",
"Bahar", "Bahavia", "Bahkan", "Bakaslavia",
"Balamkadar", "Baki", "Balinderry", "Balochistan",
"Baltish", "Baltonia", "Bataniland, Republic of",
"Bayview", "Banania, Republica de", "Bandrika",
"Bangalia", "Bangstoff", "Bapetikosweti", "Baracq",
"Baraza", "Barataria", "Barclay Islands",
"Barringtonia", "Bay View", "Basenji"
};
}
}
<html xmlns="https://www.w3.org/1999/xhtml"
xmlns:h="https://java.sun.com/jsf/html"
xmlns:util="https://java.sun.com/jsf/composite/util">
<h:body>
<h:form>
<h:panelGrid columns="2">
#{msgs.locationsPrompt}
<util:autoComplete value="#{user.country}"
completionItems="#{autoComplete.countries}" />
</h:panelGrid>
</h:form>
</h:body>
</html>
| 屬性 | EL 表達式 | 綁定目標 | 方向 |
|---|---|---|---|
| `value` | `#{user.country}` | `User.country` (String) | 雙向 (輸入 → model、選中 → model) |
| `completionItems` | `#{autoComplete.countries}` | `AutoComplete.getCountries()` (String[]) | 單向 (組件讀取初始數據) |
| 作用域 | 生命週期 | 典型用途 | 本組件角色 |
|---|---|---|---|
| `@RequestScoped` | 單次 HTTP 請求 | 控制器、臨時計算 | 不適用 (需跨請求保持值) |
| `@ViewScoped` (JSF) | 同一 View 多次 POST | CRUD 頁面、分頁表格 | 可替代 SessionScoped (若無跨頁需求) |
| `@SessionScoped` | 用戶會話 | 用戶偏好、購物車、登錄狀態 | `User` Bean — 跨請求保持選擇 |
| `@ApplicationScoped` | 應用啟動到關閉 | 配置、緩存、靜態數據源、服務門面 | `AutoComplete` Bean — 共享國家列表 |
| `@ConversationScoped` | 用戶標記的長會話 | 向導、多步驟表單 | 進階場景可替代 SessionScoped |
若完成項數據量大 (萬級) 或需遠程查詢,`AutoComplete` 可改為:
@Named
@ApplicationScoped
public class AutoComplete {
@Inject CountryService countryService; // 統一數據訪問介面
private Cache> prefixCache; // Caffeine 緩存
public List getCountries(String prefix) { // 改為帶參方法
if (prefix == null || prefix.length() < 2) return Collections.emptyList();
return prefixCache.get(prefix, k -> countryService.findByPrefix(k));
}
}
Facelet 需配合修改:`completionItems="#{autoComplete.countries['Al']}"` (EL 不支援帶參方法,需在組件內部 JS/Java 協調調用,見監聽器篇)。
兩個精簡的 CDI Bean (`User`、`AutoComplete`) 完美詮釋了 **「關注點分離」**:會話級模型負責用戶狀態,應用級提供者負責共享數據。配合 JSF 2 EL 表達式的直觀綁定,組件使用者無需關心數據獲取細節。下篇將進入組件核心 — Facelet 復合組件定義 (`autoComplete.xhtml`),展示如何將 HTML、JS、Ajax、Java 監聽器編織成可重用標籤。