View(design.html): https://github.com/habuma/spring-in-action-6-samples/blob/main/ch05/taco-cloud/src/main/resources/templates/design.html
... <form th:method="POST" th:object="${taco}" th:action="@{/design}" id="tacoForm"> <span class="validationError" th:if="${#fields.hasErrors('ingredients')}" th:errors="*{ingredients}">Ingredient Error</span> ... public class DesignTacoController { ... @ModelAttribute(name = "taco") public Taco taco() { return new Taco(); } @GetMapping public String showDesignForm() { return "design"; } ... @PostMapping public String processTaco( @Valid Taco taco, Errors errors, @ModelAttribute TacoOrder order) { log.info(" --- Saving taco"); if (errors.hasErrors()) { return "design"; } Taco saved = tacoRepo.save(taco); order.addTaco(saved); return "redirect:/orders/current"; } } As i know, a new Taco object named taco will be created and added to the model due to @ModelAttribute(name = "taco") annotation before the method. This is fine because we need a empty Taco object for the GET /design request(showDesignForm). But when we submit the form, a fresh new taco IS AGAIN created & added to the model and because we do NOT add @ModelAttribute to the processTaco's taco parameter(@Valid Taco taco), the view will get a fresh new taco which is not populated by the submitted form data. So in this case, the view was not supposed to display the current/changed taco info(i.e. changed fields or error info) correctly. But i tested and found it works.
I've search a lot about @ModelAttribute and could not find any answer to the above confusion.