You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Thymeleaf绑定对象列表失败,optionRequests接收为null求助

问题描述

即使与当前问题无关,也请指出代码中存在的错误。

我参考一篇关于Thymeleaf绑定对象列表的文章,用模态框添加表格行和input标签实现功能,但绑定失败,optionRequests始终为null。我遗漏了什么?不清楚问题出在哪里。


HTML & JavaScript

<form id="example-form" enctype="multipart/form-data">
  <input type="text" class="form-control" id="title" name="title" required>
  <input type="file" accept="image/*" class="form-control" id="imgFile" name="imgFile">
    
  <input type="button" value="Add Option" class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#optionModal">
  <!-- 此处有多余的</div>标签,需移除 -->
  <div class="modal fade" id="optionModal" tabindex="-1" aria-labelledby="optionModalLabel" aria-hidden="true">
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-header">
          <h1 class="modal-title fs-5" id="optionModalLabel">option</h1>
          <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
        </div>
        <div class="modal-body">
            <input type="text" class="form-control" id="optionName">
            <input type="text" class="form-control" id="addValue">
            <input type="text" class="form-control" id="method">
            <input type="text" class="form-control" id="material">
        </div>
        <div class="modal-footer">
          <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
          <button type="button" class="btn btn-primary" onclick="addOptionRow()">Add</button>
        </div>
      </div>
    </div>
  <table class="table table-bordered" id="optionTable">
     <thead>
      <tr>
        <th>Name</th>
        <th>AddValue</th>
        <th>Method</th>
        <th>Material</th>
        <th></th>
      </tr>
      </thead>
      <tbody>

      </tbody>
  </table>
  <!-- 缺少表单提交按钮 -->
</form>
<script>
  // 建议维护独立计数器,避免删除行后索引混乱
  let optionIndex = 0;

  function addOptionRow() {
      const table = document.getElementById('optionTable');
      const newRow = table.insertRow();
      const optionNameCell = newRow.insertCell(0);
      const addValueCell = newRow.insertCell(1);
      const methodCell = newRow.insertCell(2);
      const materialCell = newRow.insertCell(3);
      const removeButtonCell = newRow.insertCell(4);

      const optionNameText = $('#optionName').val();
      const addValueText = $('#addValue').val();
      const methodText = $('#method').val();
      const materialText = $('#material').val();

      optionNameCell.innerText = optionNameText;
      addValueCell.innerText = addValueText;
      methodCell.innerText = methodText;
      materialCell.innerText = materialText;
      removeButtonCell.innerHTML = "<button class='btn btn-danger btn-sm' onclick='removeRow(this)'>X</button>"

      // 修复:直接生成符合Spring绑定规则的name属性,Thymeleaf属性无法在客户端JS中解析
      $("#example-form").append($(`<input type='hidden' name='optionRequests[${optionIndex}].optionName' value='${optionNameText}'>`));
      $("#example-form").append($(`<input type='hidden' name='optionRequests[${optionIndex}].addValue' value='${addValueText}'>`));
      $("#example-form").append($(`<input type='hidden' name='optionRequests[${optionIndex}].method' value='${methodText}'>`));
      $("#example-form").append($(`<input type='hidden' name='optionRequests[${optionIndex}].material' value='${materialText}'>`));

      optionIndex++;

      $('#optionName').val("");
      $('#addValue').val("");
      $('#method').val("");
      $('#material').val("");
      $('#optionModal').modal('hide');
    }

  function removeRow(btn) {
      btn.closest('tr').remove();
  }
</script>

DTO定义

FormRequest

public record FormRequest(
        String title,
        List<OptionRequest> optionRequests,
        MultipartFile imgFile
) {}

OptionRequest

public record OptionRequest(
        String optionName,
        Integer addValue,
        String method,
        String material
) {
}

Controller实现

@GetMapping("/example")
public String exampleGet(Model model) {
    // 修复:统一使用FormRequest作为模型对象,避免类型不匹配
    model.addAttribute("example", new FormRequest(null, new ArrayList<>(), null));
    return "form";
}

@PostMapping("/example")
public String examplePost(
        @ModelAttribute("example") FormRequest formRequest,
        Model model
) {
    // 处理提交逻辑
    return "form";
}

Thymeleaf解耦逻辑

<?xml version="1.0"?>
<thlogic>
    ...
    <attr sel="#example-form" th:object="${example}" th:action="/example" th:method="post">
        <attr sel="#title" th:value="*{title} ?: _"/>
    ...
</thlogic>

核心问题与修复总结

  1. Thymeleaf属性无法在客户端JS中生效:你在JS里动态添加的th:field、th:name是服务端渲染属性,客户端执行JS时不会被解析,导致input没有正确的name属性,Spring无法绑定列表。需直接生成符合规则的name:optionRequests[index].propertyName。
  2. 索引计算逻辑缺陷:依赖表格行数计算索引,删除行后会出现索引断层,需用独立计数器维护连续索引。
  3. HTML结构错误:存在多余的</div>标签,会破坏DOM结构,需移除;缺少表单提交按钮,无法触发提交。
  4. 模型对象类型不匹配:Get请求用FormResponse,Post请求接收FormRequest,虽字段相同但易引发绑定问题,需统一类型。
  5. 类型转换风险:addValue对应Integer类型,前端input是text类型,需添加前端数字校验,避免提交非数字导致绑定失败。

内容的提问来源于stack exchange,提问作者jadekim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 15:24:52