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

如何通过Bootstrap模态框表单传递数据更新表格行(Spring+Thymeleaf)

解决Bootstrap模态框中Thymeleaf表达式countryToUpdate.id为null的问题

问题根源

你碰到的EL1007E: Property or field 'id' cannot be found on null错误,本质是两个核心问题:

  1. 页面首次加载时,findAll方法未给Model添加countryToUpdate属性,Thymeleaf渲染表单th:action时,${countryToUpdate}为null,自然找不到id属性。
  2. 点击编辑按钮时,findById用了@ResponseBody返回JSON,但这是AJAX请求,不会触发页面重新渲染,Model里的countryToUpdate根本不会更新,模态框仍使用初始的null值。

解决方案

方案一:非AJAX页面跳转方式

这种方式无需编写JS,通过页面跳转传递countryToUpdate属性,触发模态框显示。

1. 修改Controller

去掉findById的@ResponseBody,改为返回页面,同时添加模态框显示标记:

@GetMapping("countries/findById/{id}")
public String findById(@PathVariable int id, Model model) {
    Country country = countryService.findById(id).get();
    model.addAttribute("countryToUpdate", country);
    // 标记需要显示模态框
    model.addAttribute("showEditModal", true);
    // 重新查询国家列表,保证页面数据完整
    List<Country> listOfCountries = countryService.findAll();
    model.addAttribute("countries", listOfCountries);
    return "country";
}

2. 修改页面代码

  • 编辑按钮去掉data-toggle和data-target,保留跳转链接:
<a th:href="@{/countries/findById/{id} (id=${country.id})}" class="btn btn-primary editModalBtn">Edit</a>
  • 添加页面加载时自动显示模态框的JS:
<script th:if="${showEditModal}">
    $(document).ready(function() {
        $('#editModal').modal('show');
    });
</script>
  • 表单绑定countryToUpdate属性,输入框用th:field自动填充:
<form th:action="@{/countries/update/{id} (id =${countryToUpdate.id})}" method="post" th:object="${countryToUpdate}">
    <div class="form-group">
        <label for="descriptionEdit" class="col-form-label">Description</label>
        <input type="text" class="form-control" id="descriptionEdit" name="description" th:field="*{description}">
    </div>
    <div class="form-group">
        <label for="capitalEdit" class="col-form-label">Capital</label>
        <input type="text" class="form-control" id="capitalEdit" name="capital" th:field="*{capital}">
    </div>
    <!-- 其余输入框同理添加th:field="*{code}"、th:field="*{continent}"等 -->
</form>

方案二:AJAX异步交互方式

这种方式保留AJAX请求,用JS手动填充表单并设置action,避免初始渲染时的null问题。

1. 修改Controller

保留@ResponseBody,无需往Model加countryToUpdate:

@GetMapping("countries/findById/{id}")
@ResponseBody
public Country findById(@PathVariable int id) {
    return countryService.findById(id).get();
}

同时在findAll方法里添加空的countryToUpdate,避免Thymeleaf渲染报错:

@GetMapping("countries")
public String findAll(Model model){
    List<Country> listOfCountries = countryService.findAll();
    model.addAttribute("countries", listOfCountries);
    // 添加空对象,防止Thymeleaf初始渲染报错
    model.addAttribute("countryToUpdate", new Country());
    return "country";
}

2. 修改页面代码

  • 编辑按钮改为按钮,绑定国家ID:
<button class="btn btn-primary editModalBtn" data-id="${country.id}">Edit</button>
  • 表单去掉th:action,用JS动态设置:
<form id="editForm" method="post" th:object="${countryToUpdate}">
    <!-- 输入框保持原有结构,无需th:field -->
    <div class="form-group">
        <label for="descriptionEdit" class="col-form-label">Description</label>
        <input type="text" class="form-control" id="descriptionEdit" name="description">
    </div>
    <!-- 其余输入框不变 -->
</form>
  • 添加JS处理点击事件:
<script>
    $(document).on('click', '.editModalBtn', function() {
        const countryId = $(this).data('id');
        // AJAX获取国家数据
        $.get('/countries/findById/' + countryId, function(country) {
            // 填充表单字段
            $('#descriptionEdit').val(country.description);
            $('#capitalEdit').val(country.capital);
            $('#codeEdit').val(country.code);
            $('#continentEdit').val(country.continent);
            $('#nationalityEdit').val(country.nationality);
            // 设置表单提交地址
            $('#editForm').attr('action', '/countries/update/' + country.id);
            // 显示模态框
            $('#editModal').modal('show');
        });
    });
</script>

总结

  • 方案一适合简单场景,无需JS,依赖页面跳转传递数据。
  • 方案二更适合交互流畅的单页场景,用AJAX异步处理数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 04:48:16