1

I have recently started working with JSPs in Spring framework and I tried implementing a form handling page.

I created a basic form page that takes user input and after they hit submit they receive a confirmation page with the input that they entered.

These are the two mapped methods I have in my controller class:

import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.redcup.model.Person;
import com.redcup.model.User;
import com.redcup.test.ServletTestBean;

import javax.annotation.PostConstruct;

@Controller
public class RedCupController {

    private final Logger logger = LoggerFactory.getLogger(RedCupController.class);

    @PostConstruct
    public void intializeComplete() { 
        logger.info("Controller initialized");
    }

    @RequestMapping(method=RequestMethod.GET, value="/health")
    @ResponseBody
    public String getHealth()
    {
        logger.info("GET /health invoked.");

        return "hello world!";
    }

    @RequestMapping(method=RequestMethod.GET, value="/welcome")
    public String getWelcome(Model model) { 
        User user = new User();
        model.addAttribute("userform", user);
        return "welcome"; 
    }

    @RequestMapping(method=RequestMethod.POST, value="/confirmation")
    public String getConfirmation(@ModelAttribute("userform") User user, @ModelAttribute("pew") User user2, 
                                @ModelAttribute("whomp")Person person, Model model) {

        user = getUserForm();
        user2 = getUserForm2();

        System.out.println("hello: " + user);
        System.out.println("goodbye: " + user2);

        System.out.println("user: " + user.getFirstName());
        System.out.println("user: " + user.getLastName());
        System.out.println("user: " + user.getEmail());
        System.out.println("user: " + user.getAge());

        System.out.println("user2: " + user2.getFirstName());
        System.out.println("user2: " + user2.getLastName());
        System.out.println("user2: " + user2.getEmail());
        System.out.println("user2: " + user2.getAge());

        System.out.println("person: " + person.getName());
        System.out.println("person: " + person.getNum2());

        model.addAttribute("user", user);
        model.addAttribute("user2", user2);
        model.addAttribute("person", person);

        return "confirmation";
    }

    @ModelAttribute("userform")
    public User getUserForm() {
        return new User();
    }

    @ModelAttribute("userform2") 
    public User getUserForm2() { 
        return new User();
    }
}

The User class for the model:

public class User {

private String firstName;
private String lastName;
private String email;
private int age; 

public User() { 
    firstName = "John";
    lastName =  "Smith";
    email = "default email";
    age = 1; 
}

public void setFirstName(String firstName) {
    this.firstName = firstName;
}

public String getFirstName () { 
    return firstName;
}

public void setLastName(String lastName) { 
    this.lastName = lastName;
}

public String getLastName() { 
    return lastName;
}

public void setEmail(String email) { 
    this.email = email;
}

public String getEmail() { 
    return email;
}

public void setAge (int age) {
    this.age = age;
}

public int getAge() { 
    return age;
}

}

And below is the form entry page welcome.jsp:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
    <div align="center">

        <form:form action="confirmation" method="post" commandName="userform">
            <form:input path="firstName"/>
            <form:input path="lastName"/>
            <form:input path="email"/>
            <form:input path="age"/>
            <input type="submit" value="Press this to submit"/>
        </form:form>
    </div>
</body>
</html>

In my controller handler: getConfirmation I have two User parameters (user and user2).The part I am confused about is that when I click submit in my welcome.jsp, user and user2 are populated with the same exact values from the Model in the welcome.jsp. I'm not sure why this happens as I thought that the @ModelAttribute will only assign the corresponding model attribute to the specified parameter (i.e. user will receive the model under the commandName userform).

But to me, it doesn't seem like the @ModelAttribute annotation is doing anything. The user and user2 parameters will be populated with the same field values (what I entered into the form) when the @ModelAttributes have different keys (i.e. userform vs userform2) and when I even remove the @ModelAttribute annotations, user and user2 will STILL be popoulated with the same field values.

Can someone explain to why this is occurring and what exactly the @ModelAttribute annotation does?

Thanks in advance!

EDIT

I have created a UserWrapper class per Alan's Hay's suggestion:

public class UserWrapper {

    private User user1;
    private User user2; 

    public User getUser1() { 
        return user1;
    }

    public User getUser2() { 
        return user2; 
    }

    public void setUser1(User user) { 
        user1 = user;
    }

    public void setUser2(User user) { 
        user2 = user;
    }

    public void setUser1FirstName(String firstName) {
        user1.setFirstName(firstName);
    }

    public String getUser1FirstName() { 
        return user1.getFirstName();
    }

    public void setUser2FirstName(String firstName) {
        user2.setFirstName(firstName);
    }

    public String getUser2FirstName() { 
        return user2.getFirstName();
    }
}

and updated my welcome.jsp with the following form:

    <form:form action="confirmation" method="post" commandName="userform">
        <form:input path="user1.firstName"/>
        <form:input path="user1.lastName"/>
        <form:input path="user1.email"/>
        <form:input path="user1.age"/>
        <input type="submit" value="Press this to submit"/>
    </form:form>

1 Answer 1

2

That would be the expected behaviour.

The important point to note here is that if you look at the generated HTML of your edit page you will see that is has inputs like:

<input name="firstName"  type="text" value="user">
<input name="lastName"  type="text" value="7702">

viz. there is nothing explicitly there to tie it to any particular instance of a model object. Now, on form submission, the Spring handler will look at what you have specified as model attributes in your handler method and will attempt to bind the submitted parameters to matching fields in these model objects regardless of the qualifiers - which is what you see.

To get the behaviour you expect you would need to wrap the two model attributes something like the below:

Create a wrapper as the form backing object:

public class UserWrapper(){

    private User user1;
    private User user2;

    public User getUser1(){
        return user1;
    }

    public User getUser2(){
        return user2;
    }
}

Specifically bind the form fields only to User1

<form:form action="confirmation" method="post" commandName="userWrapper">
    <form:input path="user1.firstName"/>
    <form:input path="user1.lastName"/>
    <form:input path="user1.email"/>
    <form:input path="user1.age"/>
    <input type="submit" value="Press this to submit"/>
</form:form>

Update the handler:

@RequestMapping(method=RequestMethod.POST, value="/confirmation")
public String getConfirmation(@ModelAttribute UserWrapper wrapper) {

    System.out.println("user: " + wrapper.getUser1().getFirstName());
    System.out.println("user: " + wrapper.getUser2().getFirstName());

    return "confirmation";
}
Sign up to request clarification or add additional context in comments.

6 Comments

Should the return type of getUserForm() and getUserForm2() be User? I changed the return type to User and in getConfirmation I added these two lines: user = getUserForm(); user2 = getUserForm2(); I still ended up getting the same field values for both objects, and the user and user2 objects do not reflect the input I made in the welcome.jsp; they just show the default values from the User constructor.
You will need to post the full controller and your User class.
I have updated the original post with the full controller and included the User class. Thanks for your help in this!
I updated my original post with the UserWrapper and my new welcome.jsp per your suggestion. However, I'm now receiving an exception: javax.el.PropertyNotFoundException: Property 'firstName' not found on type com.redcup.model.UserWrapper. I think I may be creating the getters and setters incorrectly for the UserWrapper? It seems like it wants the UserWrapper to have the property firstName? Any suggestions?
You don't need the other getters and setters. Spring will bind to nested properties.i.e. <form:input path="user1.firstName"/> binds to userWrapper.getUser1().setFirstName(x). When are you getting this? On page render or on form submission? How is your updated controller?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.