Issue
I'm creating a html page with a form containing only one field: cardId
. The user needs to fill in this field with a card reader (keyboard like input). When cardId
validation fails, the user may try again (e.g. an incomplete read). Naturally, this is only possible if the previous input is cleared.
I'm using the following dependencies in my gradle build
compile("org.springframework.boot:spring-boot-starter-web")
compile("org.springframework.boot:spring-boot-starter-thymeleaf")
Two RequestMappings:
@RequestMapping(value = "/search", method = RequestMethod.GET)
public String cardSearch(CardSearch cardSearch) {
return "search";
}
@RequestMapping(value = "/search", method = RequestMethod.POST)
public String cardResult(@Valid CardSearch cardSearch, BindingResult result, Model model) {
if(result.hasErrors()) {
return "search";
} else {
...
}
}
Form object
public class CardSearch {
@CustomValidator(message = "Card not found...")
private String cardId;
Getter and setter
}
I'm using a custom validator to validate the cardId
@Override
public boolean isValid(String cardField, ConstraintValidatorContext cxt) {
Card card = find card by cardField;
if(card == null) {
return false;
} else {
return true;
}
}
Form
<div th:errors="${cardSearch.cardId}"></div>
<form action="#" th:action="@{search}" th:object="${cardSearch}" method="post">
<input type="text" th:field="*{cardId}" autofocus="autofocus" size="50" showPassword="true" />
</form>
When validation fails, the error message is shown to the user, but the fautly cardId is in the input field. One possible solution is to use a password input field (the browsers clears the input), but then the input is masked. What better way to clear the cardId
?
[Edit] added github example https://github.com/JohanBas/FormReset
Solution
EDIT : add precisions on :
- not loosing error messages
- Thymeleaf problem
You have to reset the cardId
in the model, and the attribute name must match what you use in your form (and that you didn't say ...)
You can do it explicitely :
@RequestMapping(value = "/search", method = RequestMethod.POST)
public String cardResult(@Valid CardSearch cardSearch, BindingResult result, Model model) {
if(result.hasErrors()) {
model.addAttribute("how_you_call_it_in_your_form", new CardSearch());
return "search";
} else {
...
}
}
But as you have changed the object, result
not longer points to a model attribute and will not be used for displaying eventual errors. It is better to have spring do it automatically :
@RequestMapping(value = "/search", method = RequestMethod.POST)
public String cardResult(@Valid @ModelAttribute("how_you_call_it_in_your_form") CardSearch cardSearch,
BindingResult result, Model model) {
if(result.hasErrors()) {
carSearch.setId(null);
return "search";
} else {
...
}
}
But Thymeleaf was there : There is one sentence in Thymeleaf doc that intrigued me : Values for th:field attributes must be selection expressions ( *{...} ), which makes sense given the fact that they will be evaluated on the form-backing bean and not on the context variables (or model attributes in Spring MVC jargon).
What that means is that Thymeleaf will simply ignore all modifications to the bean that can occur in the controller !
If you want to make it work, you must not use th:field
but the less magical th:value
in your form
<input type="text" name="field" th:value="${formObject.field}" autofocus="autofocus" size="50" />
It is far less Thymeleaf-styled but at least it works
I admit I am not a Thymeleaf expert, and maybe there are cleaner ways to achieve this ...
Answered By - Serge Ballesta
Answer Checked By - Marie Seifert (JavaFixing Admin)