在Spring中将动态列表从View传递到Controller

8e2ybdfx  于 2023-03-22  发布在  Spring
关注(0)|答案(1)|浏览(87)

我试图传递一个大小未知的“经验”对象列表。用户可以在FE上添加他想要的经验,所以我不知道对象的数量。
这是控制器的一部分:

@PostMapping("/buildcv")
 public ResponseEntity<byte[]> buildcv(@ModelAttribute("experience") ExperienceDTO experience{
   ...
}

DTO:

public class ExperienceDTO {
    private List<Experience> experienceList;

    //getters and setters
}

体验实体:

@Entity
public class Experience {
    @Id
    @GeneratedValue
    private int id;
    private String company;
    private String position;
    private String responsibilities;
    private Date startDate;
    private Date endDate;

    //getters and setters

观点:

<div class="work-expirience-fields" id="work-expirience">
                            <a class="remove-image" id="close-button">&#215;</a>
        
                            <div class="col-one-row-one">
                                <label>Job title</label>
                                <input th:field="${experience.position}" id="jobTitle">
                            </div>

                            <div class="col-two-row-one">
                                <label>Employer</label>
                                <input th:field="${experience.company}" id="employer">
                            </div>

                            ....

                        </div>
                     </div>

如果只有一个Experience对象传递给控制器,而不是DTO,那么这个例子就可以工作。但是,有一个按钮可以复制这个最外层的div,其中包含所有的标签。
我尝试使用spring:bind为每个字段设置一个路径,但是列表没有填充到控制器中,我找不到一个例子来描述我想要做的事情:

<spring:bind path="experience.experienceList[0]">
                            <a class="remove-image" id="close-button">&#215;</a>

                            <div class="col-one-row-one">
                                <label>Job title</label>
                                <input th:field="${experience.position}" id="jobTitle">
                            </div>

.......
qjp7pelc

qjp7pelc1#

输入名称必须被索引,以便它们绑定到模型对象中的List对象(即ExperienceDTO)。
重要的一点是,使用@Entity对象作为View对象并不是一个好的做法。您应该有一个单独的POJO并从EntityMap它。但是让我们忽略这一点进行讨论。
你的Thymeleaf模板应该是这样的

<form th:action="@{/buildcv}" th:object="${experience}" method="post">
    <div th:each="experienceItem, expState : *{experienceList}">
        <div>
            <label>Job Title</label>
            <input th:field="*{experienceList[__${expState.index}__].position}"/>
        </div>
        <div>
            <label>Employer</label>
            <input th:field="*{experienceList[__${expState.index}__].company}"/>
        </div>
        
        ......
        
    </div>
</form>

这将导致HTML页面上的第一个div为:

<div>
    <div>
        <label>Job Title</label>
        <input id="experienceList0.position" name="experienceList[0].position" value="">
    </div>
    <div>
        <label>Employer</label>
        <input id="experienceList0.company" name="experienceList[0].company" value="">
    </div>

    ...

</div>

第二行是

<div>
    <div>
        <label>Job Title</label>
        <input id="experienceList1.position" name="experienceList[1].position" value="">
    </div>
    <div>
        <label>Employer</label>
        <input id="experienceList1.company" name="experienceList[1].company" value="">
    </div>

    ...

</div>

正如您所看到的,name属性值是相应的索引,
experienceList[0].positionexperienceList[0].company
以及
experienceList[1].positionexperienceList[1].company
form元素中的th:object="expereince"是引用ExperienceDTO对象的模型属性名称。理想情况下(但不是必需的,这取决于您如何到达表单页面)应该有一个单独的GetMapping控制器方法,用于加载表单,在重定向到视图之前设置模型对象。

.....
model.addAttribute("experience", experienceDTO);
....
return "buildCvForm";

假设buildCvForm.html是您的thymeleaf模板名称。
您提到可以在前端中在页面上添加行,我假设它可能是通过使用一些前端框架,如jQuery,Angular或其他东西。不管前端中使用了什么,当添加新行时,他们必须坚持name s的索引。让我们说一个用户已经有2个Experience s的页面加载和前端-当添加第3行时结束,则输入name s必须为
experienceList[2].positionexperienceList[2].company
只要name的每个Experience字段集都被正确地索引,表单数据就会被正确地发送给控制器,以便将它们绑定到ExperienceDTO模型属性中的List中。
参考Thymeleaf文档

相关问题