【发布时间】:2021-10-22 03:33:07
【问题描述】:
我有一个服务器端呈现网页的 Spring Boot 应用程序。应用程序的其中一种形式无法正常工作。表单将正确呈现页面,并将值绑定到输入。当一个值发生变化时,表单提交时控制器收到的值是原来的。
我有一个CardDTO 对象,它具有以下内容:
@Data
@NoArgsConstructor
public class CardDTO {
private int cardId;
// Other fields
private List<CustomValueDTO> customValues;
}
作为card 属性添加到模型中。
表格如下所示:
<form method="post" action="#" th:action="${#httpServletRequest.requestURI}" th:object="${card}">
<div class="row" th:if="${editType == 'notes'}">
<div class="col-12 text-right">
<button class="btn btn-primary ml-4" type="submit" th:text="#{app.savebtn.label}">SAVE</button>
</div>
</div>
<div th:switch="${editType}">
<!-- other cases -->
<div th:case="'notes'" th:insert="card-form-section-notes :: notes-form"></div>
</div>
</form>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div th:fragment="notes-form" id="notes-edit-form" class="pr-3">
<div class="row text-left" th:each="i: *{#numbers.sequence(0, customValues.size() - 1)}" th:if="*{customValues}">
<div th:switch="*{customValues[__${i}__].type.name()}">
<div th:case="LETTER" class="form-group md-form">
<textarea rows="1" type="text"
class="form-control custom-textarea"
th:field="*{customValues[__${i}__].value}" placeholder="Enter letters only"
th:errorclass="invalid"></textarea>
<label th:for="*{customValues[__${i}__].value}" th:text="*{customValues[__${i}__].label}"></label>
<small class="text-danger" th:if="${#fields.hasErrors('customValues[__${i}__].value')}"
th:errors="*{customValues[__${i}__].value}">Error</small>
</div>
<!-- more inputs -->
</div>
</div>
</div>
</body>
</html>
控制器方法看起来像:
@Controller
@SessionAttributes({"card"})
public class CardCreateEditController {
@PostMapping(value = {"/cards/{cardUUID}/{updateType}/edit","/locations/{locationUUID}/cards/{cardUUID}/{updateType}/edit"})
public String submitChildCardEdit(@AuthenticationPrincipal OidcUser currentUser, @PathVariable String updateType, @PathVariable String cardUUID, @PathVariable Optional<String> locationUUID, @ModelAttribute("card") @Valid final CardDTO editCardDTO, final BindingResult bindingResult, final Model model, final SessionStatus status) {
// logic here
}
}
所以问题是当表单更新时,与它一起发送的@ModelAttribute("card") @Valid final CardDTO editCardDTO 参数仍然具有页面加载时editCardDTO.customValues(i) 中的原始值。
在 Chrome 中查看网络请求表明它正在提交带有更新值的表单,以及这些表单参数:
customValues[0].value: database1234
customValues[1].value: test
...
我已尝试将 CustomValueDTO 中的所有字段添加为隐藏输入,这些字段也作为表单参数提交,但仍未更改。
这与我拥有的另一种形式几乎完全相同,而且效果很好。我似乎无法弄清楚为什么这个没有。
更新 - 带有完整的 HTML:
card-edit-form.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div th:fragment="card-form-content">
<div class="card">
<!-- Card body -->
<div class="card-body">
<p class="h4 mb-3" th:text="#{${'myCompany.header.edit.'+editType}}">EDIT</p>
<form method="post" action="#" th:action="${#httpServletRequest.requestURI}" th:object="${card}"
autocomplete="off" enctype="application/x-www-form-urlencoded">
<div class="row" th:if="${editType == 'notes'}">
<div class="col-12 text-right">
<a th:if="${locationObj==null}" href="#"
th:href="@{/cards/{cardUUID}/cancel(cardUUID=${{cardUUID}})}"
th:text="#{myCompany.cancelbtn.label}" tabindex="-1">CANCEL</a>
<a th:if="${locationObj!=null}" href="#"
th:href="@{/locations/{locationUUID}/cards/{cardUUID}/cancel(cardUUID=${{cardUUID}},locationUUID=${{locationObj.locationUUID}})}"
th:text="#{myCompany.cancelbtn.label}" tabindex="-1">CANCEL</a>
<button class="btn btn-primary ml-4" type="submit" th:text="#{myCompany.savebtn.label}">SAVE</button>
</div>
</div>
<h6 class="mb-4" th:if="${#messages.msgOrNull('myCompany.subheader.edit.'+editType)!=null}"
th:text="#{${'myCompany.subheader.edit.'+editType}}">SUBTITLE</h6>
<hr>
<div th:switch="${editType}">
<div th:case="'ico'" th:insert="card-form-section-ico :: ico-form"></div>
<div th:case="'info'" th:insert="card-form-section-info :: info-form"></div>
<!--/* <div th:case="'address'" th:insert="card-form-section-address :: address-form"></div> */-->
<div th:case="'health'" th:insert="card-form-section-health :: health-form"></div>
<div th:case="'resideswith'" th:insert="card-form-section-resideswith :: resideswith-form">
</div>
<div th:case="'notes'" th:insert="card-form-section-notes :: notes-form"></div>
</div>
<div th:if="${editType != 'notes'}">
<div class="row mt-5 align-bottom">
<div class="col-12 text-right">
<a th:if="${locationObj==null}" href="#"
th:href="@{/cards/{cardUUID}/cancel(cardUUID=${{cardUUID}})}"
th:text="#{myCompany.cancelbtn.label}" tabindex="-1">CANCEL</a>
<a th:if="${locationObj!=null}" href="#"
th:href="@{/locations/{locationUUID}/cards/{cardUUID}/cancel(cardUUID=${{cardUUID}},locationUUID=${{locationObj.locationUUID}})}"
th:text="#{myCompany.cancelbtn.label}" tabindex="-1">CANCEL</a>
<button class="btn btn-primary ml-4" type="submit"
th:text="#{myCompany.savebtn.label}">SAVE</button>
</div>
</div>
</div>
</form>
</div> <!-- card body -->
</div> <!-- card -->
<div th:if="${editType != 'notes'}">
<div class="mt-4 text-center">
<small><a href="#" th:text="#{myCompany.learnmore.part1}" data-toggle="modal"
data-target="#whyWeAskModal">LEARN MORE</a> <span th:text="#{myCompany.learnmore.part2}">ABOUT WHY WE
ASK FOR THIS INFO.</span></small>
</div>
</div>
<div th:insert="components/card-why-we-ask-for-info :: why-we-ask-modal"></div>
</div> <!-- fragment -->
</body>
</html>
card-form-section-notes.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div th:fragment="notes-form" id="notes-edit-form" class="pr-3">
<h4 class="mt-3" th:if="*{customValues == null}" th:text="#{myCompany.section.cards.customfields.edit.noneexistmsg}">NONE EXIST YET.</h4>
<div class="row text-left" th:each="customValue, itemStat : *{customValues}" th:if="*{customValues}">
<div th:switch="*{customValues[__${itemStat.index}__].type.name()}">
<!-- <input type="hidden" th:field="*{customValues[__${i}__].id}">-->
<!-- <input type="hidden" th:field="*{customValues[__${i}__].uuid}">-->
<!-- <input type="hidden" th:field="*{customValues[__${i}__].type}">-->
<!-- <input type="hidden" th:field="*{customValues[__${i}__].label}">-->
<!-- <input type="hidden" th:field="*{customValues[__${i}__].customField}">-->
<div th:case="LETTER" class="form-group md-form">
<textarea rows="1" type="text"
class="form-control custom-textarea"
th:field="*{customValues[__${itemStat.index}__].value}" placeholder="Enter letters only"
th:errorclass="invalid"></textarea>
<!-- <label th:for="*{customValues[__${itemStat.index}__].value}" th:text="*{customValues[__${itemStat.index}__].label}"></label>-->
<!-- <small class="text-danger" th:if="${#fields.hasErrors('customValues[__${itemStat.index}__].value')}"-->
<!-- th:errors="*{customValues[__${itemStat.index}__].value}">Error</small>-->
</div>
<!-- <div th:case="NUMBER" class="md-form form-group">-->
<!-- <input type="number" th:id="'customValue' + ${i}" th:field="*{customValues[__${i}__].value}"-->
<!-- class="form-control" th:errorclass="invalid" placeholder="Enter numbers only">-->
<!-- <label th:for="'customValue' + ${i}" th:text="*{customValues[__${i}__].label}">NAME</label>-->
<!-- <small class="text-danger" th:if="${#fields.hasErrors('*{customValues[__${i}__].value}')}"-->
<!-- th:errors="*{customValues[__${i}__].value}">Error</small>-->
<!-- </div>-->
<!-- <div th:case="YESNO" class="form-group md-form yes-no-input">-->
<!-- <span class="label-multi" th:text="*{customValues[__${i}__].label}">YES/NO</span>-->
<!-- <div class="yes-no-check pb-2">-->
<!-- <div class="form-check form-check-inline" th:each="b : ${allBooleanTypes}">-->
<!-- <input class="form-check-input" type="radio"-->
<!-- th:field="*{customValues[__${i}__].value}" th:value="${b}" />-->
<!-- <label class="form-check-label" th:for="*{customValues[__${i}__].value}"-->
<!-- th:text="#{${b==true ? 'myCompany.yes' : 'myCompany.no'}}">Wireframe</label>-->
<!-- </div>-->
<!-- <small class="text-danger" th:if="${#fields.hasErrors('*{customValues[__${i}__].value}')}"-->
<!-- th:errors="*{customValues[__${i}__].value}">Error</small>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div th:case="PHONE" class="md-form form-group">-->
<!-- <input type="text" th:id="'customValue' + ${i}" th:field="*{customValues[__${i}__].value}"-->
<!-- class="form-control phone-text" th:errorclass="invalid" placeholder="###-###-####">-->
<!-- <label th:for="'customValue' + ${i}" th:text="*{customValues[__${i}__].label}">NAME</label>-->
<!-- <small class="text-danger" th:if="${#fields.hasErrors('*{customValues[__${i}__].value}')}"-->
<!-- th:errors="*{customValues[__${i}__].value}">Error</small>-->
<!-- </div>-->
<!-- <div th:case="DATE" class="md-form form-group">-->
<!-- <input type="text" th:id="'customValue' + ${i}" th:field="*{customValues[__${i}__].value}"-->
<!-- class="form-control child_dob_input_date" th:errorclass="invalid" placeholder="YYYY-MM-DD">-->
<!-- <label th:for="'customValue' + ${i}" th:text="*{customValues[__${i}__].label}">NAME</label>-->
<!-- <small class="text-danger" th:if="${#fields.hasErrors('*{customValues[__${i}__].value}')}"-->
<!-- th:errors="*{customValues[__${i}__].value}">Error</small>-->
<!-- </div>-->
<!-- <div th:case="YEARMONTH" class="md-form form-group">-->
<!-- <input type="text" th:id="'customValue' + ${i}" th:field="*{customValues[__${i}__].value}"-->
<!-- class="form-control child_dob_input_YM" th:errorclass="invalid" placeholder="YYYY-MM">-->
<!-- <label th:for="'customValue' + ${i}" th:text="*{customValues[__${i}__].label}">NAME</label>-->
<!-- <small class="text-danger" th:if="${#fields.hasErrors('*{customValues[__${i}__].value}')}"-->
<!-- th:errors="*{customValues[__${i}__].value}">Error</small>-->
<!-- </div>-->
<!-- <div th:case="ANY" class="form-group md-form">-->
<!-- <textarea rows="1" type="text" th:name="'customFields.customField' + ${i}"-->
<!-- th:id="'customValue' + ${i}" class="form-control custom-textarea"-->
<!-- th:field="*{customValues[__${i}__].value}" placeholder="Enter any letters"-->
<!-- th:errorclass="invalid"></textarea>-->
<!-- <label th:for="'customValue' + ${i}" th:text="*{customValues[__${i}__].label}">NAME</label>-->
<!-- <small class="text-danger" th:if="${#fields.hasErrors('*{customValues[__${i}__].value}')}"-->
<!-- th:errors="*{customValues[__${i}__].value}">Error</small>-->
<!-- </div>-->
</div>
<!--</div>-->
</div>
</div> <!-- fragment -->
</body>
</html>
【问题讨论】:
-
你解决了吗?不是一些缓存吗?你在数据库中直接看到了吗?
-
查看对象最终的 BindingResult bindingResult 是否有任何绑定错误: if (bindingResult.hasErrors()) { // 错误处理 }
-
另外,请分享完整的 html,您在哪里使用 th:field="*{customValues}" 绑定 customValues 对象???
-
@RhadamezGindriHercilio 它不是任何缓存,因为我正在
@Controller上的@PostMapping方法中进行调试,而@ModelAttribute("card") @Valid final CardDTO editCardDTO上的值没有被更新。 -
@ZeeshanArif 没有任何绑定错误。
customValues的绑定来自th:object="${card}",因为CardDTO具有customValues成员。
标签: spring spring-boot spring-mvc thymeleaf