Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ModelAndView.addObject vs Model.addAttribute

Tags:

spring-mvc

Good day, I'm learning Spring MVC and I'm writting my tiny webapp following this tutorial but I slightly modified it as a "list of tasks" and not "list of users". One thing is not clear to me so I'd like to ask for an explanation. This is my edit.jsp:

<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>  
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>  
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<html>
<head>
    <title>Edit task</title>
</head>
<body>
    <h1>Edit task</h1>
    <form:form method="post" action="/update" modelAttribute="task">
        <table>
            <tr>
                <td>Title</td>
                <td><form:input path="title"/></td>
            </tr>
            <tr>
                <td>Description</td>
                <td><form:textarea path="description"/></td>
            </tr>
            <tr>
                <td></td>
                <td><input type="submit" value="Save"/></td>
            </tr>
        </table>
    </form:form>
</body>
</html>

And this is method editTask in my HomePageController.java (version 1):

 @RequestMapping("/edit")  
 public ModelAndView editTask(@RequestParam String id, @ModelAttribute Task task) {  
   // Retrieve task from the database
     task = taskService.getTask(id);
     ModelAndView model = new ModelAndView("edit");
     model.addObject("task", task);
     return model;
 }

If I click on edit link to edit an item (e.g. /TaskBook/edit?id=1) a form appears but it is not populated. So I changed the method this way (version 2):

 @RequestMapping("/edit")  
 public String editTask(@RequestParam String id, Model model) {
     Task task = taskService.getTask(id);
     model.addAttribute("task", task);
     return "edit";
 }

Now the form is populated when I edit a task. I don't understand what's the difference between model.addObject("task", task) in version 1 and model.addAttribute("task", task) in version 2. Is the task object stored somewhere else or is it lost? Please explain. I use Spring Framework 3.2.1.

Thank you in advance. Vojtech.

EDIT: If I change editTask() to code below it works - the form is populated. But what if some task data were submited, how can I read them now?

@RequestMapping("/edit")  
public ModelAndView editTask(@RequestParam String id) {  
  task = taskService.getTask(id);
  ModelAndView model = new ModelAndView("edit");
  model.addObject("task", task);
  return model;
}
like image 390
Vojtech Avatar asked Oct 04 '13 14:10

Vojtech


People also ask

What is the difference between model and ModelAndView?

Model defines a holder for model attributes and is primarily designed for adding attributes to the model. ModelMap is an extension of Model with the ability to store attributes in a map and chain method calls. ModelAndView is a holder for a model and a view; it allows to return both model and view in one return value.

What is ModelAndView addObject?

public ModelAndView addObject(String attributeName, @Nullable Object attributeValue) Add an attribute to the model. Parameters: attributeName - name of the object to add to the model (never null ) attributeValue - object to add to the model (can be null ) See Also: ModelMap.addAttribute(String, Object) , getModelMap()

What is model addAttribute?

Model addAttribute(String attributeName, Object attributeValue) It adds the supplied attribute under the supplied name. Map<String,Object> asMap() It returns the current set of model attributes as a Map.

What is the use of model addAttribute () in spring?

Furthermore, there's also an addAttributes() method. Its purpose is to add values in the Model that'll be identified globally. That is, every request to every controller method will return a default value as a response. We also have to annotate the specific class as @ControllerAdvice.


1 Answers

You've hit an edge case that doesn't occur very often. Let's go by try

@RequestMapping("/edit")  
public String editTask(@RequestParam String id, Model model) {
    Task task = taskService.getTask(id);
    model.addAttribute("task", task);
    return "edit";
}

In this case, Spring will create a Model object from its ModelAndViewContainer and pass that as an argument to your method. So if you had model attributes added earlier, they would be available to you here and the ones you add in will be available later. You return a String view name. Spring will use that String with a ViewResolver to resolve which jsp or other type of view to render or forward to.

With this

@RequestMapping("/edit")  
public ModelAndView editTask(@RequestParam String id, @ModelAttribute Task task) {  
    // Retrieve task from the database
    task = taskService.getTask(id);
    ModelAndView model = new ModelAndView("edit");
    model.addObject("task", task);
    return model;
}

Spring, because of the @ModelAttribute, will create a Task object and pass that as an argument when invoking (reflection) your method. The ModelAndView object you create, add to, and return will be merged with the ModelAndView object contained in the ModelAndViewContainer that is managed by Spring for your request. So things you add here will also be available later.

The hitch: It appears ModelAttribute has priority on the model attributes, so it doesn't get overwritten by the model attributes you add to the ModelAndView object. Actually it gets written to your ModelAndView object, overwriting your "task" attribute. Remember that if you don't specify a value attribute to @ModelAttribute annotation, it uses the type of the argument to give it a name. For example, Task ==> "task", List<Task ==> taskList, etc.

like image 93
Sotirios Delimanolis Avatar answered Sep 28 '22 00:09

Sotirios Delimanolis