使用th:field绑定列表数据时出现Neither BindingResult错误求助
问题:表单提交时抛出
Neither BindingResult nor plain target object for bean name 'rows[0]'错误 尝试创建可提交保存的表格表单,运行时抛出错误:
Neither BindingResult nor plain target object for bean name 'rows[0]' available as request attribute
已定位问题出在th:field中使用rows的部分,但不知如何修复。
相关代码示例
实体类代码
//Entity class public class TimeTable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) protected Integer id; String day; @Column(name="start_time") String startTime; @Column(name = "end_time") String endTime; // 补充带参构造方法(原代码调用了该构造,需显式声明) public TimeTable() {} public TimeTable(String day, String startTime, String endTime) { this.day = day; this.startTime = startTime; this.endTime = endTime; } }
控制器代码
//controller class @GetMapping("/timetable/new") public String newTimetable(Model model) { List<TimeTable> rows = new ArrayList<>(); rows.add(new TimeTable("Monday", "", "")); rows.add(new TimeTable("Tuesday", "", "")); rows.add(new TimeTable("Wednesday", "", "")); rows.add(new TimeTable("Thursday", "", "")); // 修正原拼写错误Thusday rows.add(new TimeTable("Friday", "", "")); rows.add(new TimeTable("Saturday", "", "")); rows.add(new TimeTable("Sunday", "", "")); //model.addAttribute("timetableObject", new TimeTable()); model.addAttribute("rows", rows); return "timetable/timetable_new"; }
Thymeleaf视图代码
<form th:action="@{/timetable/save}" method="post" > <table class="table table-striped table-hover table-responsive-sm align-middle" id="tableRefresh"> <thead class=" text-center"> <tr> <th scope="col">Day</th> <th scope="col">Start Time</th> <th scope="col">End Time</th> </tr> </thead> <tbody> <tr th:each="row, rowStat : ${rows}"> <td>[[${row.day}]]</td> <td> <input type="text" name="startTime" th:field="*{rows[__${rowStat.index}__].startTime}" class="form-control"> </td> </tr> </tbody> </table> <button type="submit" class="form-control btn btn-outline-success">Save</button> </form>
错误栈信息
Caused by: java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'rows[0]' available as request attribute at org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:153) at org.springframework.web.servlet.support.RequestContext.getBindStatus(RequestContext.java:926) at org.thymeleaf.spring6.context.webmvc.SpringWebMvcThymeleafRequestContext.getBindStatus(SpringWebMvcThymeleafRequestContext.java:232) at org.thymeleaf.spring6.util.FieldUtils.getBindStatusFromParsedExpression(FieldUtils.java:306) at org.thymeleaf.spring6.util.FieldUtils.getBindStatus(FieldUtils.java:253) at org.thymeleaf.spring6.util.FieldUtils.getBindStatus(FieldUtils.java:227) at org.thymeleaf.spring6.processor.AbstractSpringFieldTagProcessor.doProcess(AbstractSpringFieldTagProcessor.java:174) at org.thymeleaf.processor.element.AbstractAttributeTagProcessor.doProcess(AbstractAttributeTagProcessor.java:74) ... 116 more
解决方案
问题原因
Thymeleaf的th:field使用选择表达式*{}时,需要表单绑定一个根对象(通过th:object指定),选择表达式会基于这个根对象进行属性查找。原代码中没有指定th:object,直接用集合rows作为绑定目标,导致Thymeleaf错误地尝试查找名为rows[0]的请求属性,从而抛出异常。
修复步骤
1. 创建表单包装类
创建一个包含List<TimeTable>属性的包装类,作为表单绑定的根对象:
public class TimeTableForm { private List<TimeTable> rows; // Getter和Setter public List<TimeTable> getRows() { return rows; } public void setRows(List<TimeTable> rows) { this.rows = rows; } }
2. 修改控制器代码
将集合包装到TimeTableForm中,添加到Model作为表单绑定对象:
@GetMapping("/timetable/new") public String newTimetable(Model model) { List<TimeTable> rows = new ArrayList<>(); rows.add(new TimeTable("Monday", "", "")); rows.add(new TimeTable("Tuesday", "", "")); rows.add(new TimeTable("Wednesday", "", "")); rows.add(new TimeTable("Thursday", "", "")); rows.add(new TimeTable("Friday", "", "")); rows.add(new TimeTable("Saturday", "", "")); rows.add(new TimeTable("Sunday", "", "")); TimeTableForm form = new TimeTableForm(); form.setRows(rows); model.addAttribute("timeTableForm", form); // 绑定根对象 return "timetable/timetable_new"; }
3. 修改Thymeleaf视图
在<form>标签中添加th:object="${timeTableForm}"指定根对象,同时修正th:field的表达式,去掉手动设置的name属性(th:field会自动生成正确的name):
<form th:action="@{/timetable/save}" method="post" th:object="${timeTableForm}"> <table class="table table-striped table-hover table-responsive-sm align-middle" id="tableRefresh"> <thead class=" text-center"> <tr> <th scope="col">Day</th> <th scope="col">Start Time</th> <th scope="col">End Time</th> </tr> </thead> <tbody> <tr th:each="row, rowStat : ${timeTableForm.rows}"> <td>[[${row.day}]]</td> <td> <input type="text" th:field="*{rows[__${rowStat.index}__].startTime}" class="form-control"> </td> <td> <input type="text" th:field="*{rows[__${rowStat.index}__].endTime}" class="form-control"> </td> </tr> </tbody> </table> <button type="submit" class="form-control btn btn-outline-success">Save</button> </form>
4. 添加保存接口的控制器方法
接收TimeTableForm对象,处理表单提交的数据:
@PostMapping("/timetable/save") public String saveTimetable(@ModelAttribute TimeTableForm form) { // 遍历form.getRows(),将每个TimeTable保存到数据库 // 示例:timeTableRepository.saveAll(form.getRows()); return "redirect:/timetable"; // 保存后重定向到列表页 }
可选临时方案(不推荐)
如果不想创建包装类,可以直接使用变量表达式${}替代选择表达式*{},手动设置name属性实现参数绑定,但这种方式无法利用Spring表单绑定的验证、错误提示等功能:
<input type="text" name="rows[__${rowStat.index}__].startTime" th:value="${row.startTime}" class="form-control">
内容的提问来源于stack exchange,提问作者Solomon Otoo
相关产品推荐
相关产品推荐

