Spring Boot中Thymeleaf动态表单迭代索引始终为0的问题
问题
我尝试通过列表phases生成表格,每行的表单对应该行的列表项phase——第一行单元格显示“Phase 1”及重命名按钮,第二行显示“Phase 2”及对应按钮。但从表单内访问迭代索引时始终返回0:第二行单元格显示正常,但表单提交时仍传递第一行的索引;若改为phaseStat.size能得到正确长度,仅在<form>标签内索引停留在第一个列表项。显示phase名称的单元格工作正常,如何让表单迭代与表格其余部分同步?
代码如下:
<table class="table"> <tr th:each="phase : ${phases}"> <td class="table-light" th:text="${phase}"></td> <td> <form th:action="@{/tasks/phases/rename}"> <button type="button" class="btn btn-outline-primary btn-sm" title="Rename" onclick="openForm('renamephaseform')">Rename</button> <div class="form-popup card mb-3" id="renamephaseform"> <h3 class="card-header">Rename <button type="button" class="btn btn-outline-secondary btn-sm closebutton" title="Close menu" onclick="closeForm('renamephaseform')">⛌</button> <br> </h3> <div class="card-body"> <input type="text" class="form-control" id="phasename" placeholder="Phase name" th:name="newname"> <button type="submit" class="btn btn-primary" title="Rename" th:name="phasenumber" th:value="${phaseStat.index}">Rename <span th:text="${phase}"></span></button> </div> </div> </form> </td> </tr> </table> <script> function openForm(name) { document.getElementById(name).style.display = "block"; } function closeForm(name) { document.getElementById(name).style.display = "none"; } </script>
编辑补充:
奇怪的是,以下示例中的迭代完全正常,我看不出两者的区别……
<table class="table"> <tr th:each="album : ${albums}"> <td class="table-light" th:text="${album.name} + ' (' + ${album.artist} + ')'"></td> <td> <form th:action="@{/index}"> <button type="submit" class="btn btn-primary btn-sm" th:name="id" th:value="${album.id}">Open album</button> </form> </td> </tr></table>
解决方案
核心问题是所有弹窗的id重复了——HTML要求页面内id必须唯一。你循环生成的每一行都用了同一个renamephaseform作为弹窗id,点击第二行的Rename按钮时,openForm只会找到第一个匹配的弹窗(也就是第一行的那个),提交时自然触发的是第一行的表单,传递的也是第一行的索引。而你补充的示例没有弹窗,每个表单独立,不存在id冲突,所以能正常工作。
修复步骤如下:
- 给每个弹窗生成唯一id,结合迭代索引区分:
<div class="form-popup card mb-3" th:id="'renamephaseform-' + ${phaseStat.index}">
- 修改打开/关闭按钮的函数调用,传入对应索引的唯一id:
<button type="button" class="btn btn-outline-primary btn-sm" title="Rename" th:onclick="'openForm(\'renamephaseform-' + ${phaseStat.index} + '\')'">Rename</button>
<button type="button" class="btn btn-outline-secondary btn-sm closebutton" title="Close menu" th:onclick="'closeForm(\'renamephaseform-' + ${phaseStat.index} + '\')'">⛌</button>
- (可选优化)如果不想拼接字符串,可通过自定义属性传递id:
给Rename按钮添加data-form-id属性:
<button type="button" class="btn btn-outline-primary btn-sm" title="Rename" th:data-form-id="'renamephaseform-' + ${phaseStat.index}" onclick="openForm(this)">Rename</button>
修改JS函数:
function openForm(elem) { const formId = elem.getAttribute('data-form-id'); document.getElementById(formId).style.display = "block"; }
关闭按钮同理:
<button type="button" class="btn btn-outline-secondary btn-sm closebutton" title="Close menu" th:data-form-id="'renamephaseform-' + ${phaseStat.index}" onclick="closeForm(this)">⛌</button>
function closeForm(elem) { const formId = elem.getAttribute('data-form-id'); document.getElementById(formId).style.display = "none"; }
修复后的完整代码示例:
<table class="table"> <tr th:each="phase, phaseStat : ${phases}"> <td class="table-light" th:text="${phase}"></td> <td> <form th:action="@{/tasks/phases/rename}"> <button type="button" class="btn btn-outline-primary btn-sm" title="Rename" th:onclick="'openForm(\'renamephaseform-' + ${phaseStat.index} + '\')'">Rename</button> <div class="form-popup card mb-3" th:id="'renamephaseform-' + ${phaseStat.index}"> <h3 class="card-header">Rename <button type="button" class="btn btn-outline-secondary btn-sm closebutton" title="Close menu" th:onclick="'closeForm(\'renamephaseform-' + ${phaseStat.index} + '\')'">⛌</button> <br> </h3> <div class="card-body"> <input type="text" class="form-control" id="phasename" placeholder="Phase name" th:name="newname"> <button type="submit" class="btn btn-primary" title="Rename" th:name="phasenumber" th:value="${phaseStat.index}">Rename <span th:text="${phase}"></span></button> </div> </div> </form> </td> </tr> </table> <script> function openForm(name) { document.getElementById(name).style.display = "block"; } function closeForm(name) { document.getElementById(name).style.display = "none"; } </script>
内容的提问来源于stack exchange,提问作者Nick Siepmann
相关产品推荐
相关产品推荐

