Thymeleaf 的复选框有两种常见模型:每行保存一个 Boolean,或者把选中的值收集到集合。前者绑定真假状态,后者需要 value 标识选中项。出现“非 Boolean 字段需要 value”之类错误时,先看字段类型,不能只调整 HTML 外观。
每一行都有独立的真假值
假设后台放入名为 form 的表单对象,其中 rows 是列表,每个 Row 有 checked 属性。模板可以写成:
<form method="post" th:action="@{/choices}" th:object="${form}">
<label th:each="row, stat : *{rows}">
<input type="checkbox"
th:field="*{rows[__${stat.index}__].checked}">
<span th:text="|选项 ${stat.count}|">选项</span>
</label>
<button type="submit">提交</button>
</form>
预处理部分先算出列表索引,th:field 再得到类似 rows[0].checked 的绑定路径。这里不能只写循环变量的 checked,否则可能显示正确但提交字段名不符合后台模型。
对应的简化 Java 表单模型如下:
import java.util.ArrayList;
import java.util.List;
public class ChoiceForm {
private List<Row> rows = new ArrayList<>();
public List<Row> getRows() { return rows; }
public void setRows(List<Row> rows) { this.rows = rows; }
public static class Row {
private boolean checked;
public boolean isChecked() { return checked; }
public void setChecked(boolean checked) { this.checked = checked; }
}
}
例如控制器渲染时创建两行:
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class ChoiceController {
@GetMapping("/choices")
public String choices(Model model) {
ChoiceForm form = new ChoiceForm();
form.getRows().add(new ChoiceForm.Row());
form.getRows().add(new ChoiceForm.Row());
model.addAttribute("form", form);
return "choices";
}
}
这是渲染与字段命名的示例,提交入口需要在项目中按业务实现。Thymeleaf 对 Boolean 复选框还会生成用于处理“未选中”的隐藏标记,不能仅看浏览器没有提交 checkbox 就认为后台能自动保留正确状态。官方复选框与动态字段说明
多选集合要为每个选项提供值
若 form.selectedIds 是字符串集合,候选 items 中有 id 和 name,则写法不同:
<label th:each="item : ${items}">
<input type="checkbox" th:field="*{selectedIds}" th:value="${item.id}">
<span th:text="${item.name}">名称</span>
</label>
该片段也要放在 th:object=”${form}” 的表单中。这时 value 表示某个选项的身份,不应该为解决错误而把集合改为 Boolean。
列表索引不等于可信业务身份
前端字段可以被修改,行数也可以被扩充。后台应限制可绑定字段、列表大小,并从服务端确认允许操作的条目,不直接信任隐藏 ID 或前端行顺序。涉及金额、权限等字段时,尤其不能把整个实体开放绑定,相关边界见WebDataBinder 可绑定字段。
检查结果时同时看渲染后的 name、value、checked,以及提交后实际绑定对象,覆盖全未选、部分选中和多行场景。