When we must use @ModelAttribute, and how it works
Asked Answered
N

2

5

Hello I have question about @ModelAttribute annotation. As i understand, we use @ModelAttribute in method arguments to get data from the model. But it's quite hard to understand clearly when and how its used. (Code samples are from Spring in Action 5 book) Why in this case in the code below in public String processOrder() method we do not use @ModelAttribute annotation on @Valid Order order

    @Controller
    @RequestMapping("/orders")
    @SessionAttributes("order")
    public class OrderController {

      private OrderRepository orderRepo;

      public OrderController(OrderRepository orderRepo) {
        this.orderRepo = orderRepo;
      }

      @GetMapping("/current")
      public String orderForm(@AuthenticationPrincipal User user, 
          @ModelAttribute Order order) {
        if (order.getDeliveryName() == null) {
          order.setDeliveryName(user.getFullname());
        }
        //following conditions

        return "orderForm";
      }


      @PostMapping
      public String processOrder(@Valid Order order, Errors errors,  // <<< Here
          SessionStatus sessionStatus, 
          @AuthenticationPrincipal User user) {

        if (errors.hasErrors()) {
          return "orderForm";
        }

        order.setUser(user);

        orderRepo.save(order);
        sessionStatus.setComplete();

        return "redirect:/";
      }


    }

but in this case, DesignTacoController class, @ModelAttribute on a method processDesign() is used on @Valid Taco taco:

    @Slf4j
    @Controller
    @RequestMapping("/design")
    public class DesignTacoController {



      @PostMapping
 public String processDesign(@Valid @ModelAttribute("design") Taco design, // <<< Here   
 Errors errors, Model model) {
        if (errors.hasErrors()) {
          return "design";
        }

        // Save the taco design...
        // We'll do this in chapter 3
        log.info("Processing design: " + design);

        return "redirect:/orders/current";
      }

And then in the next chapter author removes @ModelAttribute from processDesign() method from the same DesignTacoController class.

    @Controller
    @RequestMapping("/design")
    @SessionAttributes("order")
    @Slf4j
    public class DesignTacoController {

      @ModelAttribute(name = "order")
      public Order order() {
        return new Order();
      }

      @ModelAttribute(name = "design")
      public Taco design() {
        return new Taco();
      }


      @PostMapping
      public String processDesign(
          @Valid Taco taco, Errors errors, // <<< Here 
          @ModelAttribute Order order) {

        log.info("   --- Saving taco");

        if (errors.hasErrors()) {
          return "design";
        }

        Taco saved = tacoRepo.save(taco);
        order.addDesign(saved);

        return "redirect:/orders/current";
      }

And in this code snippet(from the code above):

    @PostMapping
          public String processDesign(
              @Valid Taco taco, Errors errors, // <<< Here 
              @ModelAttribute Order order) {
    ....
    }

quote from book: "The Order parameter is annotated with @ModelAttribute to indicate that its value should come from the model and that Spring MVC shouldn’t attempt to bind request parameters to it." This I don't understand what author meant here, because in all tutorials it is said that when @ModelAttribute is used as a method arguments,it binds request parameters to it. Binds the form data with a POJO bean, model attribute is populated with data from a form submitted.

Nival answered 10/10, 2019 at 20:1 Comment(4)
For your query of quote from book, you can find answer here. https://mcmap.net/q/406365/-what-causes-quot-java-lang-illegalstateexception-neither-bindingresult-nor-plain-target-object-for-bean-name-39-command-39-available-as-request-attribute-quotEva
Possible duplicate of Spring: @ModelAttribute VS @RequestBodyBytom
1. I don't understand when we use Valid ModelAttribute Object object, and when Valid Object object is enough. 2/3. In your links it is said that "ModelAttribute is used for binding data from request param" and my quote from book says that we use ModelAttribute "that Spring shouldn’t attempt to bind request parameters". I don't get it because for me those two quotes contradicts to each otherNival
@JamesLar I faced the same problem! did you figure it out? what does the author mean by that quote?Acus
A
4

The documentation is pretty clear on this:

https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-methods

@ModelAttribute

For access to an existing attribute in the model (instantiated if not present) with data binding and validation applied. See @ModelAttribute as well as Model and DataBinder.

Note that use of @ModelAttribute is optional (for example, to set its attributes). See “Any other argument” at the end of this table.

.

Any other argument

If a method argument is not matched to any of the earlier values in this table and it is a simple type (as determined by BeanUtils#isSimpleProperty, it is a resolved as a @RequestParam. Otherwise, it is resolved as a @ModelAttribute.

So essentially it is optional. You may wish to use just to make it explicit that that is how the argument is resolved or you may need to use if binding should not happen (by specifying binding = false) See futher: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/ModelAttribute.html. It is normally my preference to specify it regardless.

Alinaaline answered 11/10, 2019 at 9:54 Comment(2)
Thank You Alan Hay. I still haven't figured out last question about qoute from book Spring in Action 5. I don't understand what author meant by saying "The Order parameter is annotated with ModelAttribute to indicate that its value should come from the model and that Spring MVC shouldn’t attempt to bind request parameters to it." But everywhere I read says that ModelAttribute is used for binding data from request parameters. Isn't that opposite to what author wrote? What I'm not getting here?Nival
I can only assume the author has prevented binding by specifying binding=false. In that case (i.e. use a model attribute as a param but with no binding) you must then specify @ModelAttribute docs.spring.io/spring/docs/current/javadoc-api/org/…Alinaaline
B
2

This wasn't clear to me either.

Here we need specify the name if the model attribute. Because in our view we assume it is named "design" and not "taco".

@PostMapping
public String processDesign(@Valid @ModelAttribute("design") Taco design, Errors errors) {

If we rename the Taco class to Design ... We don't need to specify the name if the model attribute. It will be deduced from the simple name of the class. com.example.Design -> "design"

@PostMapping
public String processDesign(@Valid Design design, Errors errors) {

See the javadoc for ModelAttribute:

The default model attribute name is inferred from the declared attribute type (i.e. the method parameter type or method return type), based on the non-qualified class name: e.g. "orderAddress" for class "mypackage.OrderAddress", or "orderAddressList" for "List".

Businesslike answered 15/12, 2019 at 13:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.