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

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')">&#9932;</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冲突,所以能正常工作。

修复步骤如下:

  1. 给每个弹窗生成唯一id,结合迭代索引区分:
<div class="form-popup card mb-3" th:id="'renamephaseform-' + ${phaseStat.index}">
  1. 修改打开/关闭按钮的函数调用,传入对应索引的唯一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} + '\')'">&#9932;</button>
  1. (可选优化)如果不想拼接字符串,可通过自定义属性传递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)">&#9932;</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} + '\')'">&#9932;</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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 19:27:16