Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Web MVC, @ModelAttribute and @RequestParam together

Tags:

spring-mvc

I have a controller with a GET method written like so:

@Controller
public class ThingController {

     @RequestMapping( value = "/Thing.html", method = RequestMethod.GET )
    public String editThing(@RequestParam( "thingId" ) String thingId, @ModelAttribute ThingBean thing, BindingResult result) {
        thing = <some service call using thingId>
        logger.debug("The thing to edit is {}", thingBean);
        return "thing/edit";
    }
}

The bean is correct (getters and setters), the service call returns the correct ThingBean with the thingId, and my JSP page at thing/edit.jsp shows up.

The JSP is:

<html>
<body>
    <h1 id="title" class="title">Edit Thing</h1>

<form:form id="thing" modelAttribute="thing">
    <form:input path="subject" id="subject" tabindex="1" />
    <form:textarea path="message" />

</form:form>

</body>
</html>

Yet, the JSP displays blank values for the subject and message. Yes, there are getters/setters on those properties.

I have a very similar Controller that works just fine, except there is no @RequestParam in the signature of the GET - mapped method there.

I've not seen anywhere in the Spring WebMVC docs that says I can't do this - why isn't it working? Why isn't the updated ModelAttribute object being bound into the JSP form?

EDIT:

This controller, and this same pattern for GET calls has worked many many different places -- the variable annotated with @ModelAttribute is filled in by the method then available for the JSP page to display. Why, by adding a @RequestParam annotation, does it stop?

@RequestMapping( value = "/Things.html", method = RequestMethod.GET )
public String getThings(@ModelAttribute ThingForm thingForm, BindingResult result) {

    try {
        // need to get list of Things
        List<Thing> Things = <service call that gets list of Things>;
        thingForm.setThings(Things);
        logger.debug("Things are {}", Things);

    }
    catch (ResourceNotFoundException e) {
        return "error";
    }

    logger.debug("Thing list loading - end");

    // Go to jsp
    return "thing/list";
}
like image 250
Jim Jarrett Avatar asked Sep 05 '13 21:09

Jim Jarrett


2 Answers

The problem is you are only assigning a new reference to thing, that is never going to work in Java. You have to put it in the model else it will not be known to your view.

@RequestMapping( value = "/Thing.html", method = RequestMethod.GET )
public String editThing(@RequestParam( "thingId" ) String thingId, @ModelAttribute ThingBean thing, BindingResult result) {
    thing = <some service call using thingId> // This is only assigning a new reference not updating 
    logger.debug("The thing to edit is {}", thingBean);
    return "thing/edit";
}

So instead do this

@RequestMapping( value = "/Thing.html", method = RequestMethod.GET )
public String editThing(@RequestParam( "thingId" ) String thingId, @ModelAttribute ThingBean thing, BindingResult result, Model model) {
    thing = <some service call using thingId>
    model.addAttribute("thing", thing);
    logger.debug("The thing to edit is {}", thingBean);
    return "thing/edit";
}

Which makes me wonder why do you even have a model attribute in this method? It is basically useless.

Instead of the above I would do something like this

@ModelAttribute("thing")
public Thing prepareModel(@RequestParam("thingId") String thingId) {
    return thingSergice.findById(thingId);
}

Now this method will be invoked before every request handling method. In which you can simply refer to it instead of having to look it up each time. (And judging from your code your Thingbean model attribute in your method is pretty useless I would rewrite that to the following)

@RequestMapping( value = "/Thing.html", method = RequestMethod.GET )
public String editThing() {
    return "thing/edit";
}
like image 113
M. Deinum Avatar answered Jan 01 '23 14:01

M. Deinum


You forgot to specify the ModelAttribute name, use following correction:

public String editThing(@RequestParam( "thingId" ) String thingId, 
@ModelAttribute("thing") ThingBean thing, BindingResult result) {
....
}

If above doesn't work use this:

public String editThing(@RequestParam( "thingId" ) String thingId, 
    @ModelAttribute("thing") ThingBean thing, BindingResult result, Model model) {
            ....
    thing = <some service call using thingId>
    logger.debug("The thing to edit is {}", thingBean);
    model.addAttribute("thing",thing);
    return "thing/edit";
}

If possible, I recommend using better naming convention for components, for example- thingModel instead of thing. This promotes better readability for you and others.

like image 32
aces. Avatar answered Jan 01 '23 15:01

aces.