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,以及提交后实际绑定对象,覆盖全未选、部分选中和多行场景。