I have controller:
@Controller
@Slf4j
@RequestMapping("/natalchart")
public class NatalChartDataController {
@GetMapping({"/data"})
public String data(Model model, NatalChartDataPayload data) {
model.addAttribute("months", Month.values());
return "natalChartData";
}
@ModelAttribute("data")
public NatalChartDataPayload natalChartDataPayload(){
return NatalChartDataPayload.builder().build();
}
@PostMapping("/create")
public String createNatalChart(
@Valid @ModelAttribute NatalChartDataPayload data,
BindingResult result
) {
if (result.hasErrors()) {
return "natalChartData";
}
// rest omitted
}
This is the template:
<div class="container" data-aos="fade-up">
<form method="post" th:action="@{/natalchart/create}" th:object="${data}">
<div class="row">
<div class="col-lg-8">
<h4>Enter your birth details to create your natal chart</h4>
<p> </p>
<p> </p>
<div th:if="${#fields.hasErrors('email')}" th:errors="*{email}">Email Error</div>
and the error:
However, I don't see the error message in the template.
First of all, I am very sorry, because I am afraid my first answer was incomplete, and you needed to ask a related question a second time.
I think the problem is related to the following method:
@PostMapping("/create")
public String createNatalChart(
@Valid @ModelAttribute NatalChartDataPayload data,
BindingResult result
) {
if (result.hasErrors()) {
return "natalChartData";
}
// rest omitted
In Spring, if you define a model attribute without defining its name explicitly it will fallback to the uncapitalized object type. Thus, when you define this:
@Valid @ModelAttribute NatalChartDataPayload data,
Spring will generate a bean with the name natalChartDataPayload
. As you presented in your screenshot, that is the bean that is being evaluated for errors in your debug window.
But, in your page you are looking for a bean with name data
, and it turns out that there is one, implicitly included by Spring in every request, because you defined it with this method:
@ModelAttribute("data")
public NatalChartDataPayload natalChartDataPayload(){
return NatalChartDataPayload.builder().build();
}
To solve the issue, you can use something like the following:
@Controller
@Slf4j
@RequestMapping("/natalchart")
public class NatalChartDataController {
@GetMapping({"/data"})
public String data(Model model, NatalChartDataPayload data) {
model.addAttribute("months", Month.values());
model.addAttribute("data", NatalChartDataPayload.builder().build());
return "natalChartData";
}
@PostMapping("/create")
public String createNatalChart(
@Valid @ModelAttribute("data") NatalChartDataPayload data,
BindingResult result
) {
if (result.hasErrors()) {
// you can inject model and include the data object as an attribute
// as well, although it is redundant after defining @ModelAttribute("data")
return "natalChartData";
}
// rest omitted
}
Please, note that I named your ModelAttribute
as data
:
@Valid @ModelAttribute("data") NatalChartDataPayload data,
and now everything matches the data
name.
Probably it will be enough to solve the issue, although in the presented code I removed the natalChartDataPayload
method as well, I think it may cause confusion, and I added the data
attribute in your data
method instead.
Similar problems and solutions have been provided here in SO too: see for example this one, or this other.
There is a related Github issue that could be of help as well.