Announcement Announcement Module
Collapse
No announcement yet.
Migrating old controllers to Spring 3, question about forms and @ModelAttribute Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Migrating old controllers to Spring 3, question about forms and @ModelAttribute

    I'm starting to migrate our old SimpleFormController-based web app to a Spring 3 annotation based approach, and had a question about forms and the @ModelAttribute annotation.

    I'm trying to set up some "expensive" support data to render on the page alongside the form, and at first I was annotating a method with @ModelAttribute like so:

    Code:
    @Controller
    public class SampleController {
    
        private UserService userService;
    
        @RequestMapping(method = RequestMethod.GET)
        public String initForm( @ModelAttribute("command") SampleForm command, HttpServletRequest request, Model model ) {
    
            return "/users/editForm";
        }
    
        @RequestMapping(method = RequestMethod.POST)
        public String processSubmit( @ModelAttribute("command") @Valid SampleForm command, BindingResult errors, HttpServletRequest request, Model model ) throws Exception {
    
            if (errors.hasErrors()) {
                // Validation failed, return to form
                return "/users/editForm";
            }
    
            // Finish processing submit
            return "/users/successPage";
        }
    
        @ModelAttribute("userChoices")
        public List<User> getUserChoices() {
    
            List<User> users = userService.getSomeData();
            return users;
        }
    
        public void setUserService( UserService userService ) {
            this.userService = userService;
        }
    }
    But I noticed that getUserChoices() is being called before the GET and the POST. Turns out, I only need that data on the page during the initial GET or if there were errors in the POST and I need to send the user back to the form. If the post ends up being successful and I forward them to a success page, then fetching that model data was unnecessary; it will never be used. So I cooked up a helper method, populateModel(), that adds this data to the Model in the case of a GET or an error. Is there a "Spring way" to do this, or am I on the right track?

    Code:
    @Controller
    public class SampleController {
    
        private UserService userService;
    
        @RequestMapping(method = RequestMethod.GET)
        public String initForm( @ModelAttribute("command") SampleForm command, HttpServletRequest request, Model model ) {
    
            populateModel( model );
            return "/users/editForm";
    
        }
    
        @RequestMapping(method = RequestMethod.POST)
        public String processSubmit( @ModelAttribute("command") @Valid SampleForm command, BindingResult errors, HttpServletRequest request, Model model ) throws Exception {
    
            if (errors.hasErrors()) {
                // Validation failed, return to form
                populateModel( model );
                return "/users/editForm";
            }
    
            // Finish processing submit
            return "/users/successPage";
        }
    
        private void populateModel( Model model ) {
    
            List<User> users = userService.getSomeData();
            model.addAttribute( "userChoices", users );
    
            return;
        }
    
        public void setUserService( UserService userService ) {
            this.userService = userService;
        }
    }

  • #2
    Hello

    I suggest you read carefully 15. Web MVC framework

    Since you are working with a POJO that should be reflected like a form in your jsp file you must consider work with @SessionAttributes too, see this section:

    15.3.2.4 Binding request parameters to method parameters with @RequestParam

    There is more snippet code working with this annotation throughout this chapter

    I think you are confusing the real purpose for @ModelAttribute

    Let me know your advance
    Last edited by dr_pompeii; Jul 18th, 2011, 01:46 PM.

    Comment


    • #3
      We may be getting mixed up with what I'm trying to highlight in my sample, so let's take a look at a PetClinic example:

      Code:
      @Controller
      @RequestMapping("/editPet.do")
      @SessionAttributes("pet")
      public class EditPetForm {
      
      	private final Clinic clinic;
      
      	@Autowired
      	public EditPetForm(Clinic clinic) {
      		this.clinic = clinic;
      	}
      
      	@ModelAttribute("types")
      	public Collection<PetType> populatePetTypes() {
      		return this.clinic.getPetTypes();
      	}
      
      	@RequestMapping(method = RequestMethod.GET)
      	public String setupForm(@RequestParam("petId") int petId, Model model) {
      		Pet pet = this.clinic.loadPet(petId);
      		model.addAttribute("pet", pet);
      		return "petForm";
      	}
      
      	@RequestMapping(method = RequestMethod.POST)
      	public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result, SessionStatus status) {
      		new PetValidator().validate(pet, result);
      		if (result.hasErrors()) {
      			return "petForm";
      		}
      		else {
      			this.clinic.storePet(pet);
      			status.setComplete();
      			return "redirect:owner.do?ownerId=" + pet.getOwner().getId();
      		}
      	}
      }
      In this code, it appears as if the author is setting up some data for the Model under "types", which appears to be a Collection of enums that may be used as options in a selectbox, or perhaps used in a label or somewhere else on the page.

      My question with this code pertains to the @ModelAttribute("types") annotation for populatePetTypes(). Let's say for the sake of my example that this.clinic.getPetTypes() is an expensive method call. Spring will invoke the populatePetTypes() method before the GET (setupForm()) and before the POST (processSubmit()) methods in his controller. Now, for the GET and in the case of a POST validation error, that's great because the "petForm" view page will be rendered, and the collection of PetType object will be used.

      But if the post succeeds, however, there will be a redirection to "redirect:owner.do..." and fetching the collection of PetTypes will have been unnecessary.

      Is there any way to get finer control over the population of model data in a controller like this?

      Comment

      Working...
      X