Search code examples
javavalidationstruts2fieldrepopulation

Struts2 Repopulate form information when validation fails


I have a page with a form that is prepopulated with user information. It is a user profile page. I have validation for some of the fields in place but currently my validation method is just hard coded to execute addFieldError("exp", "Cats"); where exp is a variable that is being validated and Cats is a random message. The form has selects and a doubleselect which I am repopulating by executing actions in the jsp. (Seen Below)

This is the entire form:

<s:form action="%{formAction}" method="post" enctype="multipart/form-data">
    <div id="buttons">
        <s:submit value="Save" />
    </div>

    <div id="left_column" class="divStyle">
            <div id="picture">
                <div id="picture_border">
                    Picture should go here 150px x 150px
                </div>
                <s:file name="upload" label="File" />
            </div>

            <hr />  

            <div id="contact" class="divPad">
                <h3>Contact Information</h3>
                <s:textfield name="email" value="%{profile.email}" required="true" />
            </div>

            <hr />

            <div id="availabilityDuration" class="divPad">
                <h3>When Available</h3>
                    <s:textfield name="whenAvailable" value="%{profile.whenAvailable}" />

                <h3>Availability Length</h3>
                <s:textfield name="availabilityLength" value="%{profile.availabilityLength}" />

                <h3>Desired Credit</h3>
                <s:action name="CreditSelectAction" executeResult="true" />
            </div>
        </div>


        <div id="right_column" class="divStyle">
            <div id="basic_info" class="divPad">
            <h4>College & Major</h4>
            <s:action name="CollegeMajorsSelectAction" executeResult="true" />
            <hr />
            <h4>Years of Work Experience</h4>
            <s:action name="ExpYearsSelectAction" executeResult="true" />               <hr />
            <h4>Undergrad</h4>
            <s:action name="UndergradSelectAction" executeResult="true" />              <hr />
            <h4>Graduation Year</h4>
            <s:action name="GradYearSelectAction" executeResult="true" />
        </div>

        <hr />

        <div id="aboutDescription" class="divPad">
            <h3>About Me</h3>
                <s:textarea name="comments" value="%{profile.comments}" cols="40" rows="10" />
        </div>

        <hr />

        <div id="skillsNeeds" class="divPad">
            <h3>Skills</h3>
            <div id="userSkillList">
                <s:iterator value="profile.skills" status="status">
            <div>
                    <h5 class="formLabels">Skill Description</h5>
                <s:textfield name="userSkillDescription_%{#status.count}" value="%{description}" />

                <h5 class="formLabels">Years of Experience</h5>
                <s:textfield name="userSkillExperience_%{#status.count}" value="%{experience}"/>

                <h5 class="removeSkillLink" onclick="removeUserSkill(this);">Remove Skill</h5>
            </div>
        </s:iterator>
        <h5 class="addSkillLink" id="addSkill" onclick="addUserSkill();">Add New Skill</h5>
    </div>  
</div>

</div>
</s:form>

The dropdowns are populating alright. The problem is that the values that I thought would be saved in the value stack and retained upon reloading the jsp (%{formAction}, %{profile.email}, etc.) are not being retained when I reload the jsp. How do I capture these values and present them when the page is reloaded after the failed validation? I have tried adding them to the session, but that tends to get messy and I'm not sure how to get that to work with the formAction.

Code from struts.xml:

<action name="updateProfile" class="profiles.actions.UpdateProfileAction" method="execute">
        <!-- <interceptor-ref name="basicStack"/>
        <interceptor-ref name="fileUpload">
            <param name="allowedTypes">image/jpeg,image/gif,image/png,image/jpg</param>
        </interceptor-ref> 
        <interceptor-ref name="validation"/>
        <interceptor-ref name="workflow"/> -->
        <result name="success">/index.jsp</result>
        <result name="input">/jsp/editProfile.jsp</result>
    </action>

Code snippet from the Action that loads the form:

public String execute()
{

    // get the user profile
    String result = "success";

    //If the profile is null, then the user is new and does not yet have a profile
    //NOTE: If the user's profile doesn't exist, when trying to view someone else's 
    //profile, they will be redirected to edit their own.
    if(user.intValue() == 0)
    {
        logger.info("New User Detected. Returning Failure.");
        result = "failure";
    }
    else
    {
        //If the userid is null, we are loading the user's profile
        //Otherwise, we are viewing someone else's profile

        if(userid == null)
            userid = user.toString();

        profile = dao.selectCurUserById(Integer.parseInt(userid));

        // get all of my projects
        this.setMyProjects(projectDAO.selectMyProjects(Integer.parseInt(userid)));

        // get all of the projects i've been invited to
        this.setJoinedProjects(projectDAO.selectJoinedProjects(Integer.parseInt(userid)));
    }
    return result;

}

Code snippet from that Action that updates the user profile:

public String execute()
{
    // request that sent from the client
    HttpServletRequest request = ServletActionContext.getRequest();
    Map<String, Object> session = ActionContext.getContext().getSession();

    profile = new UserProfile();
    id = ((authentication.beans.User) session.get("currentUser")).getId();
    profile.setId(id);
    profile.setEmail(email);
    profile.setAvailabilityLength(availabilityLength);
    profile.setComments(comments);
    profile.setUndergrad(Integer.parseInt(undergrad));
    profile.setWhenAvailable(whenAvailable);
    profile.setYear(year);
    profile.setCredit(credit);
    profile.setMajor(Major.getMajor(major));
    profile.setCollege(College.getCollege(college));
    profile.setExp(exp);
    SimpleDateFormat sdf = new SimpleDateFormat("dd-MMM-yy");
    profile.setModify(sdf.format(new GregorianCalendar().getTime()));

    //If file is not null, then the user is uploading an image for his/her
    //profile
    if(file != null)
    {
        byte[] b = new byte[(int) file.length()];
        try
        {
            new FileInputStream(file).read(b);
            b = Base64.encode(b);
        } 
        catch (IOException e)
        {
            // TODO Auto-generated catch block
            profile.setComments(profile.getComments() + "\n" + e.getMessage());
        }
        profile.setPic(b);
    }

    // get all of the params and then process the user skills
    HashMap<String,String[]> params = (HashMap<String,String[]>)request.getParameterMap();

    // process the user skills
    profile.setSkills(processSkills(params));

    // add the userid to the skills
    for(int i = 0; i < profile.getSkills().size(); i++){
        profile.getSkills().get(i).setUserID(profile.getId());
    }

    //TODO: Check the result and do error handling
    boolean result = dao.updateProfile(profile);

    return "success";
}

UPDATE

The problem was pretty much what coding_idiot said. In the action for loading the form, I needed to have getters for the information to initially populate the form (had this). In the action for updating the information, I needed to have setters for the information put into the form AND a getter for where to get the new information if the form should be repopulated after a failed validation. I fixed this by populating the profile object in the validate() function in the update action with the data I got from the form and then providing a setter for the profile object. I did not have to make any changes to my .jsp


Solution

  • I don't know what you've written in your action, but from above I can infer that this is a slight misunderstanding about functionality issue. You are having fields with names - email, whenAvailable etc. but their value is fetched from profile object.

    How can you expect to set it from something that doesn't exist ?

    To make it more clear : 
    Values that go from JSP to action is email, but what comes back is profile.email, instead it 
    should have been just email, or you can simply skip the value attribute or you can change your 
    field-name to profile.email
    

    Let me know if that helps.

    [EDIT]

    Reading your question again, the getters and setters for profile won't help, because what is going into the action is just primitive fields (email etc.) So you should have getters/setters for these fields instead and then to re-populate the fields into the JSP you can simply use for e.g. :

     <s:textfield name="whenAvailable" value="%{whenAvailable}" />
    

    OR

     <s:textfield name="whenAvailable"/>