Controller 處理請求後(如驗證失敗、編輯頁面),需將 Request 參數回填至前端表單,避免用戶重複輸入。本文實作一個通用工具方法,將 Map<String, Object> 序列化為 JavaScript 代碼,由前端執行完成回填。
public final class FormBackfillUtils {
/** 將參數 Map 轉為 JS 回填腳本 */
public static String toBackfillScript(Map params, String[] ignoreKeys, String callbackFn) {
StringBuilder js = new StringBuilder();
js.append("var formData = {};\n");
js.append("var fields = [];\n");
js.append("function ").append(callbackFn).append("() {\n");
for (Map.Entry entry : params.entrySet()) {
String key = entry.getKey();
Object val = entry.getValue();
if (ArrayUtils.contains(ignoreKeys, key)) continue;
if (key.startsWith("VENUS") || key.startsWith("RANMIN")) continue; // 內部參數過濾
js.append("fields.push('").append(escapeJs(key)).append("');\n");
if (val instanceof String) {
js.append("formData['").append(escapeJs(key)).append("'] = '").append(escapeJs((String) val)).append("';\n");
} else if (val instanceof String[]) {
String[] arr = (String[]) val;
js.append("formData['").append(escapeJs(key)).append("'] = [");
for (int i = 0; i < arr.length; i++) {
if (i > 0) js.append(",");
js.append("'").append(escapeJs(arr[i])).append("'");
}
js.append("];\n");
} else if (val instanceof Date) {
js.append("formData['").append(escapeJs(key)).append("'] = '").append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(val)).append("';\n");
} else if (val instanceof Number) {
js.append("formData['").append(escapeJs(key)).append("'] = ").append(val).append(";\n");
}
// 其他類型可擴展
}
js.append("for (var i=0; i
前端通用回填函數
function fillField(name) {
var el = document.querySelector('[name="' + name + '"]');
if (!el) return;
var val = formData[name];
if (val === undefined) return;
if (el.type === 'radio' || el.type === 'checkbox') {
// 群組處理
var group = document.querySelectorAll('[name="' + name + '"]');
var vals = Array.isArray(val) ? val : [val];
group.forEach(function(e) { e.checked = vals.includes(e.value); });
} else if (el.tagName === 'SELECT') {
var opts = el.options;
var vals = Array.isArray(val) ? val : [val];
for (var i=0; i
JSP 整合範例
<script>
// 後端輸出的腳本會自動執行 backfill()
<%
Map backfillMap = (Map) request.getAttribute("backfillData");
if (backfillMap != null) {
out.print(FormBackfillUtils.toBackfillScript(backfillMap, new String[]{"password"}, "backfill"));
}
%>
</script>
優勢與注意事項
- 解耦:後端只負責資料序列化,前端負責 DOM 操作,互不干擾。
- 支援多值 (checkbox group、select multiple)、日期、數字等類型。
- 需注意 XSS:工具類已做基本 JS 字串轉義,若資料含 HTML 標籤需額外處理。
- 大量欄位時考慮輸出 JSON 讓前端
JSON.parse,減少腳本體積。