You need to enable JavaScript to run this app. Thymeleaf单选按钮分组及DtoWrapper数据提交到Controller相关问题
优惠活动
大模型
产品
解决方案
定价
更多

Thymeleaf单选按钮分组及DtoWrapper数据提交到Controller相关问题

问题1:单选按钮按人员独立分组

你当前所有单选按钮同组是两个原因导致:

  • name属性没有加th:前缀,Thymeleaf不会解析${}表达式,所有按钮的name都是固定字面量${shiftDto.getFullName()},所以全部属于同一分组
  • 单选按钮的id全局重复,label的for属性无法对应到正确的输入项

修改方案:循环时引入迭代状态变量,用th:field自动生成分组用的name属性,每个人员的单选按钮name会自动生成为shifts[0].shiftCode、shifts[1].shiftCode格式,天然按人员独立分组。

问题2:选中值写入dtoWrapper对象

  • 首先给ShiftDto、DtoWrapper补全无参构造、所有属性的getter和setter,Spring MVC需要反射调用这些方法完成对象赋值
  • 表单提交方式改为post,避免get请求参数长度限制
  • 用th:field="*{shifts[__${iterStat.index}__].shiftCode}"绑定对应列表元素的shiftCode属性,Thymeleaf会自动处理选中值和对象属性的双向映射
  • 增加隐藏域绑定fullName,避免提交后Dto的fullName字段为空

问题3:download接口获取包装对象

Model是单次请求范围内有效的,你在upload接口存到Model的dtoWrapper在返回页面后就已经失效,download接口的Model是新请求的空对象,不能从里面取数据。
直接在download方法上加@ModelAttribute("dtoWrapper") DtoWrapper dtoWrapper入参,Spring MVC会自动把表单提交的参数绑定到这个对象里,直接用入参的dtoWrapper即可。


完整修改代码

1、Java类补全

public class ShiftDto {
    private String fullName;
    private int shiftCode;
    // 补无参构造
    public ShiftDto() {}
    public ShiftDto(String fullName, int shiftCode) {
        this.fullName = fullName;
        this.shiftCode = shiftCode;
    }
    // 补所有getter setter
    public String getFullName() {return fullName;}
    public void setFullName(String fullName) {this.fullName = fullName;}
    public int getShiftCode() {return shiftCode;}
    public void setShiftCode(int shiftCode) {this.shiftCode = shiftCode;}
}

public class DtoWrapper {
    private List<ShiftDto> shifts;
    // 补无参构造、getter setter
    public DtoWrapper() {}
    public List<ShiftDto> getShifts() {return shifts;}
    public void setShifts(List<ShiftDto> shifts) {this.shifts = shifts;}
}

2、前端模板修改

<form action="#" th:action="@{/download}" th:object="${dtoWrapper}" method="post">
    <fieldset>
        <legend>Select Shift information for each officer</legend>
        <ul>
            <li th:each="shiftDto, iterStat : ${dtoWrapper.getShifts()}">
                <span th:text="${shiftDto.getFullName()}"></span>
                <!-- 隐藏域保存fullName,避免提交后丢失 -->
                <input type="hidden" th:field="*{shifts[__${iterStat.index}__].fullName}" />
                <input type="radio" th:id="|ignore-${iterStat.index}|" th:field="*{shifts[__${iterStat.index}__].shiftCode}" value="0" checked>
                <label th:for="|ignore-${iterStat.index}|">Ignore</label>
                <input type="radio" th:id="|mgDay-${iterStat.index}|" th:field="*{shifts[__${iterStat.index}__].shiftCode}" value="1">
                <label th:for="|mgDay-${iterStat.index}|">MG Day</label>
                <input type="radio" th:id="|mgNight-${iterStat.index}|" th:field="*{shifts[__${iterStat.index}__].shiftCode}" value="2">
                <label th:for="|mgNight-${iterStat.index}|">MG Night</label>
                <input type="radio" th:id="|epWbDay-${iterStat.index}|" th:field="*{shifts[__${iterStat.index}__].shiftCode}" value="3">
                <label th:for="|epWbDay-${iterStat.index}|">EP WB Day</label>
                <input type="radio" th:id="|epWbNight-${iterStat.index}|" th:field="*{shifts[__${iterStat.index}__].shiftCode}" value="4">
                <label th:for="|epWbNight-${iterStat.index}|">EP WB Night</label><br>

                <input type="radio" th:id="|visRecep-${iterStat.index}|" th:field="*{shifts[__${iterStat.index}__].shiftCode}" value="5">
                <label th:for="|visRecep-${iterStat.index}|">Visitors Recep</label>
                <input type="radio" th:id="|plDay-${iterStat.index}|" th:field="*{shifts[__${iterStat.index}__].shiftCode}" value="6">
                <label th:for="|plDay-${iterStat.index}|">Plaistow Day</label>
                <input type="radio" th:id="|plNight-${iterStat.index}|" th:field="*{shifts[__${iterStat.index}__].shiftCode}" value="7">
                <label th:for="|plNight-${iterStat.index}|">Plaistow Night</label>
                <br><br>
            </li>
            <input type="submit" value="Submit" /> <input type="reset" value="Reset" />
        </ul>
    </fieldset>
</form>

3、download接口修改

@PostMapping("/download")
public ResponseEntity<ByteArrayResource> download(@ModelAttribute("dtoWrapper") DtoWrapper dtoWrapper) {
    System.out.println("Controller.download");
    try {
        String filename = createFileName();
        HttpHeaders header = new HttpHeaders();
        header.setContentType(new MediaType("application", "force-download"));
        header.set(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename);
        // 直接使用入参的dtoWrapper即可
        List<Shift> shifts = getShifts(dtoWrapper.getShifts());
        List<OutputRow> outputData = SwipeProcessor.getOutputDataFrom(allSwipes, shifts);
        ByteArrayResource resource = new ByteArrayResource(WriteOutputToXlsx.write(outputData));
        return new ResponseEntity<>(resource, header, HttpStatus.CREATED);
    } catch (Exception e) {
        return new ResponseEntity<>(HttpStatus.EXPECTATION_FAILED);
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.15 13:15:06