Spring MVC: <form:select> option won't stay selected

15,558

Your model includes an attribute title that refers to a Title class. This is not the same title you are referring to in your form, which is actually a titleId. Since the titleId is not part of the modelAttribute, it should be excluded from the <form:xxx> tags. You are going to need to use a plain-old <select> tag to pass the selected titleId back to the controller for processing. Unfortunately with a <select> tag, you can't just set the value attribute with JSTL, so you have to conditionally set the seelcted attribute of the option, based on the titleId value (if it is set). If titleList is a simple list of Title objects, you can create your <select> tag this way:

<select id="titleInput" name="titleId">
    <option value=""></option>
    <c:forEach items="${titleList}" var="title">
        <c:when test="${title.titleId== titleId}">
            <option value="${title.titleId}" selected>${title.titleName}</option>
        </c:when>
        <c:otherwise>
            <option value="${title.titleId}" >${title.titleName}</option>
        </c:otherwise>
    </c:forEach>
</select>

In your controller, the @RequestParam annotation will pull the titleId out of the submitted data. Since it is not part of the modelAttribute, you need to make sure this gets added as a model attribute:

...
if (result.hasErrors()) {
    if (titleId != null) {
        model.addAttribute("titleId", titleId);  // <--This line added
        model.addAttribute("titleList", titleService.getAll());
        Title title = titleService.get(titleId);
        teacher.setTitle(title);
        model.addAttribute("teacher", teacher);
        return "addTeacher";
    }
    else {
        model.addAttribute("titleList", titleService.getAll());
        return "addTeacher";
    }
}
...

Hopefully we got it this time.

Share:
15,558
just_a_girl
Author by

just_a_girl

Updated on June 28, 2022

Comments

  • just_a_girl
    just_a_girl almost 2 years

    I have a simple form for adding a new teacher. I'm using Spring <form:select> in my view to show a list of teacher's titles, but when I select an option without entering teacher's first and/or last name, since I'm doing validation of all three fields, when the page loads after submit, previously selected option gets lost and "Select title" text appears again.

    This is controller:

    @RequestMapping(value="/add", method = RequestMethod.POST)
    public String postAddTeacher(@RequestParam(value = "title") Integer titleId, 
            @Validated(Teacher.TeacherChecks.class) @ModelAttribute("teacherAttribute") Teacher teacher,
            BindingResult result,
            Model model) {
    
        logger.debug("Received request to add new teacher");
    
        if (result.hasErrors()) {
            if (titleId != null) {
                model.addAttribute("titleList", titleService.getAll());
                Title title = titleService.get(titleId);
                teacher.setTitle(title);
                model.addAttribute("teacher", teacher);
                return "addTeacher";
            }
            else {
                model.addAttribute("titleList", titleService.getAll());
                return "addTeacher";
            }
        }
        else {
            teacherService.add(titleId, teacher);
            return "success/addTeacherSuccess";
        }
    }
    

    This is view:

    <c:url var="saveUrl" value="/essays/main/teacher/add" />
    <form:form modelAttribute="teacherAttribute" method="POST" action="${saveUrl}">
    <form:errors path="*" cssClass="errorblock" element="div" />
    
    <form:label path="title"></form:label>
    <form:select path="title" id="titleSelect">
        <form:option value="" label="Select title" />
        <form:options items="${titleList}" itemValue="titleId" itemLabel="titleDescription" />              
    </form:select>
    <form:errors path="title" cssClass="error"/>
    
    <form:label path="firstName">First name:</form:label>
    <form:input path="firstName"/>
    <form:errors path="firstName" cssClass="error"/>
    
    <form:label path="lastName">Last name:</form:label>
    <form:input path="lastName"/>
    <form:errors path="lastName" cssClass="error"/>
    
     <input type="submit" value="Submit" />
    </form:form>
    

    Just in case this is Teacher bean:

    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "TEACHER_ID", unique = true, nullable = false)
    private Integer teacherId;
    
    @NotNull(message = "Teacher's first name is null!", groups = TeacherChecks.class)
    @NotBlank(message = "Please enter teacher's first name!", groups = TeacherChecks.class)
    @Column(name = "FIRST_NAME", nullable = false, length = 50)
    private String firstName;
    
    @NotNull(message = "Teacher's last name is null!", groups = TeacherChecks.class)
    @NotBlank(message = "Please enter teacher's last name!", groups = TeacherChecks.class)
    @Column(name = "LAST_NAME", nullable = false, length = 50)
    private String lastName;
    
    @NotNull(message = "Please choose title!", groups = TeacherChecks.class)
    @Valid
    @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch=FetchType.EAGER)
    @JoinColumn(name = "TITLE_FK", nullable = false)
    private Title title;
    
    @ManyToMany(mappedBy = "teachers")
    private Set<Activity> activities;
    
    public Teacher() {
    }
    // getters & setters
    

    I would like to keep my selected option after page reloads. I though it will happen automatically, like when I enter a value into a text field, it stays there even after the page reloads. Can someone please help me with this? Is there a way to do that from the controller, or it has to be done in the view, and how?

    Update:

    I added value="${teacherAttribute.title}" to <form:select>, as @willOEM suggested, but it still doesn't work. Now it looks like this:

    <form:select path="title" id="titleSelect" value="${teacherAttribute.title}">
        <form:option value="" label="Select title" />
        <form:options items="${titleList}" itemValue="titleId" itemLabel="titleDescription" />              
    </form:select>
    
  • just_a_girl
    just_a_girl about 10 years
    Thank you for the answer @willOEM. I added value="${teacherAttribute.title}" to my view but it still doesn't work :( Is there something I need to change in my controller?
  • woemler
    woemler about 10 years
    @just_a_girl: Please see my updated answer, there are a couple items I did not notice on my first pass.
  • just_a_girl
    just_a_girl about 10 years
    I can't remove path attribute since it's required, should I set it to path="" instead? @willOEM
  • just_a_girl
    just_a_girl about 10 years
    Now I get error Required Integer parameter 'title' is not present :( @willOEM
  • woemler
    woemler about 10 years
    My mistake. Since the titleId is not part of the modelAttribute, I don't think it can be a part of the <form:xxx> tags. You probably need to just use a standard <select> tag and use EL to populate the options. I'll try recreating this and updating my answer.
  • just_a_girl
    just_a_girl about 10 years
    Also, I thought that when the form is submitted, itemValue="titleId" is passed to the controller, that's why I have @RequestParam(value = "title") Integer titleId in controller - value attribute of the @RequestParam is set to path attribute of the <form:select>, but titleId is passed. Am I wrong? I'm a bit lost now... @willOEM I just saw your new comment, so - ok :)
  • just_a_girl
    just_a_girl about 10 years
    So I can't achieve what I want by using <form:select> at all?
  • woemler
    woemler about 10 years
    @just_a_girl: Okay, let's give this another shot. Please see my updated answer. I tested this (in a simpler app) and it worked for me.
  • just_a_girl
    just_a_girl about 10 years
    Just one more question @willOEM: what if my titleList is NOT a hash of key-value pairs, but just plain list of Title objects - List<Title>? Forgive my question, I'm a beginner.
  • woemler
    woemler about 10 years
    If this is the case, you can reference attributes of your Title objects in your JSTL loop with simple dot-notation. Eg. ${title.titleName} or ${title.titleId}
  • just_a_girl
    just_a_girl about 10 years
    Thank you so much for the effort @willOEM. Problem is if I don't use <form:select> I can't use <form:errors path="title" cssClass="error"/> either, and all the bean validation I did was for nothing :( I guess there is no simple solution for this... I will accept your answer...