Spring MVC: <form:select> option won't stay selected
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.
just_a_girl
Updated on June 28, 2022Comments
-
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 about 10 yearsThank 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 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 about 10 yearsI can't remove
path
attribute since it's required, should I set it topath=""
instead? @willOEM -
just_a_girl about 10 yearsNow I get error
Required Integer parameter 'title' is not present
:( @willOEM -
woemler about 10 yearsMy 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 about 10 yearsAlso, 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 topath
attribute of the<form:select>
, buttitleId
is passed. Am I wrong? I'm a bit lost now... @willOEM I just saw your new comment, so - ok :) -
just_a_girl about 10 yearsSo I can't achieve what I want by using
<form:select>
at all? -
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 about 10 yearsJust 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 about 10 yearsIf 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 about 10 yearsThank 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...