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

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>

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.

Related

Dynamically created Dropdown list does not return a value

I create my dropdown based on values from an enum, then try to return the value by using the th:value="${parameterName}" like for other fields, but the return value is null.
Controller get method:
#GetMapping("/createorupdatebusvehicle/{id}")
public String createBusVehicleDisplay(Model model, #PathVariable(value = "id") long id, HttpServletResponse response) throws IOException {
BusVehicle busVehicle = busVehicleRepository.findById(id).get();
if(busVehicle == null){
response.sendRedirect("/createorupdatebusvehicle");
return null;
}
model.addAttribute("busVehicleId", id);
model.addAttribute("busVehicleColor", busVehicle.getColor().toString());
model.addAttribute("busVehicleType", busVehicle.getType().toString());
// all attributes are set
return "createOrUpdateBusVehicle";
}
Page view:
<form action="#" th:action="#{/createorupdatebusvehicle}" method="post">
<input type="hidden" name="busVehicleId" th:value="${busVehicleId}" />
<p>Plate number: <input type="text" name="busVehiclePlateNumber" th:value="${busVehiclePlateNumber}" /></p>
<p>Passenger capacity: <input type="text" name="busVehiclePassengerCapacity" th:value="${busVehiclePassengerCapacity}" /></p>
//== Here are the selects ==
<select name="color">
<option th:each="colorOpt : ${T(com.grazzini.model.BusVehicleColor).values()}"
th:value="${busVehicleColor}" th:text="${colorOpt}" th:selected="${busVehicleColor} == colorOpt"></option>
</select>
<select name="type">
<option th:each="typeOpt : ${T(com.grazzini.model.BusVehicleType).values()}"
th:value="${busVehicleType}" th:text="${typeOpt}" th:selected="${busVehicleType} == typeOpt"></option>
</select>
Then get the selected value back in the controller:
#PostMapping("/createorupdatebusvehicle")
public String checkAndCreateBusVehicle (HttpServletRequest request, HttpServletResponse response) throws IOException {
String busVehicleId = request.getParameter("busVehicleId");
//...
String busVehicleColor = request.getParameter("busVehicleColor"); //null
String busVehicleType = request.getParameter("busVehicleType"); //null
/// the rest
Color and Type are enums. All other requests return the correct value, for a text field for example. Any idea why this one behave differently?
You have name="..." on the fields that are working. You need to add name="busVehicleColor" and name="busVehicleType" on your respective <select /> tags..

Java spring post form one to many relationship

I'm trying to get POST form data on controller of the object have one to many relationship. Like below code
My Notification Model
private long id;
#NotBlank(message = ERROR.NOT_EMPTY)
private String title;
#NotBlank(message = ERROR.NOT_EMPTY)
private String content;
// One Notification has many User
private List<NotificationUser> listNotificationUser;;
// Getter and setter method
My controller
#RequestMapping(value = "notification/add", method = RequestMethod.GET)
public String create(ModelMap model) {
ArrayList<User> listUser = userService.getAllUsername();
model.put("user", listUser);
model.addAttribute("notification", new Notification());
return "notification/add";
}
#RequestMapping(value = "notification/add", method = RequestMethod.POST)
public String create(ModelMap model, HttpServletRequest request,
#Valid #ModelAttribute(value = "notification") Notification notification, BindingResult bindingResult) {
....
}
My .jsp
<form:form method="POST" action="add" name="addForm" commandName="notification">
<!-- Other input -->
<form:select path="listNotificationUser" multiple="multiple" id="name" name="itemValue">
<form:options />
<form:options items="${user}" itemValue="id" itemLabel="name" />
</form:select>
<!-- Other input -->
</form:form>
When I submit POST form to controller, the field notification.listNotificationUser always is null (other field is fine).
I was search and try some solution but it not working.
I guess your problem its you have a typo in your form:select. You are defining 2 options blocks, and I guess you want to define just an empty option. So should be like this
<form:select path="listNotificationUser" multiple="multiple" id="name" name="itemValue">
<!-- Some value you can identify as empty option-->
<form:option value="0" label="--Options--"/>
<form:options items="${user}" itemValue="id" itemLabel="name" />
</form:select>

Take object from list and pass to another method

As the title describes, using thymeleaf I display the contents of a list and then I put "Update" buttons next to each item on the list that send the particular object to an editing form page.
Here is the controller method for adding the list to the list view:
#RequestMapping("/list")
public String list(Model model){
List<Employee> employees = repository.findAll();
model.addAttribute("employees", employees);
return "list";
}
And here is the thymeleaf html code:
<tr th:each="emp : ${employees}">
<td th:text="${emp.id}"></td>
<td th:text="${emp.name}"></td>
<td th:text="${emp.surname}"></td>
<td th:text="${emp.age}"></td>
<td th:text="${emp.department}"></td>
<td>
<form th:action="#{/update}" method = "POST" th:object="${emp}">
<input type="hidden" th:field="*{id}"></input>
<input type="hidden" th:field="*{name}" ></input>
<input type="hidden" th:field="*{surname}"></input>
<input type="hidden" th:field="*{age}"></input>
<input type="hidden" th:field="*{department}"></input>
<button type = "submit">Update</button>
</form>
</td>
</tr>
And here is the receiving method:
#RequestMapping("/update")
public String update(#ModelAttribute("emp") Employee emp){
return "update";
}
I keep getting the following exception:
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'emp' available as request attribute
Please let me know if you have any ideas on accomplishing this task.
This could be help.Change your controller like this.Its work for me.
model.addAttribute("emp", new Employee());
#RequestMapping("/list")
public String list(Model model){
List<Employee> employees = repository.findAll();
model.addAttribute("emp", new Employee());
model.addAttribute("employees", employees);
return "list";
}

Getting null values from checkboxes

I am working on a simple Spring MVC project. I am having trouble getting values from checkboxes. What I mean is when a user checks 2 boxes out of 3, all 3 are binded to a list with non-checked values as null. That's wrong. I just want values that are checked. Those that didn't get checked should not come to list at all.
This is a snippet of my code:
POJO:
public class Student{
private List<StudentCourses> sc;
//getters and setters
}
public class StudentCourses{
private int courseID;
private String courseName;
private Character grade;
private String semesterID;
//getters and setters
}
This is what I send from my controller:
#RequestMapping(value = "/selectclasses", method = RequestMethod.POST)
public String selectClasses(Model m) {
Student s = new Student();
List<StudentCourses> coursesList = new ArrayList<StudentCourses>();
coursesList.add(new StudentCourses("Eng 101", '-', "SP 16"));
coursesList.add(new StudentCourses("Math 140", '-', "SP 16"));
coursesList.add(new StudentCourses("CS 442", '-', "SP 16"));
m.addAttribute("coursesList", coursesList);
m.addAttribute("student", s);
return "selectclasses";
}
This is what I have in my selectclasses.jsp:
<form:form modelAttribute="student" method="post" action="/success">
<table>
<c:forEach items="${coursesList}" var="r" begin="0" varStatus="status">
<form:checkbox path="sc[${status.index }].courseName" value="${r.courseName}" label="${r.courseName}" />
</c:forEach>
</table>
<input type="submit" id="submit" name="submit" value="Submit" />
</form:form>
I don't know why null is passed to the "sc.courseName" when it's not checked. What am I doing wrong? Or is there a work around it?
Please help
Thanks.
simply writer
<input type="checkbox" value="${r.courseName}" id="id"name="name"/>
in controller class get the values using
String []values=request.getParameterValues(pass the id);
simple you get the selected values
I found the Solution!
I found two ways to solve it. This is the solution using Spring tags:
<form:checkboxes path="sc" items="${coursesList}" itemValue="courseName" itemLabel="courseName" />
In the above code, itemValue and itemLabel is the main thing! itemValue and itemLabel simply refer to bean properties of an object inside items attribute (items="${coursesList}"). In a nutshell, if you need to use a List of your Custom Beans as the items attribute you need to use also the itemValue and itemLabel attributes. This bold part of paragraph is taken from: https://stackoverflow.com/a/15529281/4828463 by #Carlos Gavidia
And now the solution using JSTL core tags:
<c:forEach items="${coursesList}" var="courses">
<tr>
<td><form:checkbox path="sc" value="${courses.courseName}" label="${courses.courseName}"/></td>
</tr>
</c:forEach>
Again the value and label attributes are important.

Spring mvc, how to bind a domain object that has a collection as its property

I have a domain object called Order, and it has a collection attribute called serviceOrders where a collection of service --- order m:m association relationships are hold.
public class Order implements Serializable {
private Long id = null;
private BigDecimal amountPaid;
private BigDecimal accountReceivable;
private User user;
private Set serviceOrders = new HashSet();
private Date closed;
private Date created = new Date();
private String status;
also there is a method for adding the association called addServiceOrder
public void addServiceOrder(ServiceOrder serviceOrder) {
if (serviceOrder == null)
throw new IllegalArgumentException("Can't add a null serviceOrder.");
this.getServiceOrders().add(serviceOrder);
}
how should I use commandName to set this collection with "path", I think it would only call its get set method of the Command Object. how should I add serviceOrder to this command Object. I have no idea about this problem. any help would be highly appreciated
Assuming your ServiceOrder instances have unique ids your service method should be #add(Long id).
Ok bear with me on this one but the solution is simple an annoying at the same time. I ran into this a couple of months ago. I am going to show you my solution using the jstl libraries in my view for handling the collections.
<c:forEach items="${Questions}" var="quest" varStatus="itemsIndex">
<fieldset>
<legend>${quest.section}</legend>
<form:form id="group${itemsIndex.index}" modelAttribute="ChoiceList" action="" method="POST" onsubmit="javascript:ajaxSave($(this).serialize()); return false;">
<a id="Group${quest.id}"></a>
<c:forEach items="${quest.qisQuestionsCollection}" var="quest2" varStatus="itemsRow">
<div style="font-weight: bold; margin: 10px 0px">${quest2.shortText}</div>
( ${quest2.qisQuestionTypes.description} )<br/>
( ${quest2.helpText} )<br/>
<a id="Question${quest2.id}"></a>
<c:choose>
<c:when test="${quest2.qisQuestionTypes.questionType == 'CHOOSEANY'}">
<c:forEach items="${quest2.qisChoicesCollection}" var="quest3" varStatus="indexStatus">
<c:forEach items="${ChoiceFields}" var="CField">
<c:set scope="request" value="${quest3}" var="ChoiceData"/>
<c:set scope="request" value="${CField}" var="ChoiceProperty"/>
<%
answerMap = (HashMap<QisChoice, Answer>) request.getAttribute("AnswerList");
choice = (QisChoice) request.getAttribute("ChoiceData");
if (answerMap.containsKey(choice.getChoiceID())) {
Answer theAnswer = (Answer) answerMap.get(choice.getChoiceID());
if (theAnswer != null) {
if (theAnswer.getChoiceValue() != null) {
request.setAttribute("itemValue", theAnswer.getChoiceValue());
request.setAttribute("itemSelected", true);
} else {
request.setAttribute("itemSelected", false);
request.setAttribute("itemValue", getReflectedValue(
(QisChoice) request.getAttribute("ChoiceData"),
(AccessorStruct) request.getAttribute("ChoiceProperty")));
}
}
} else {
request.setAttribute("itemSelected", false);
request.setAttribute("itemValue", getReflectedValue(
(QisChoice) request.getAttribute("ChoiceData"),
(AccessorStruct) request.getAttribute("ChoiceProperty")));
}
request.setAttribute("itemValue2", getReflectedValue(
(QisChoice) request.getAttribute("ChoiceData"),
(AccessorStruct) request.getAttribute("ChoiceProperty")));
%>
<c:choose>
<c:when test="${CField.visible == 'HIDDEN'}">
<form:hidden value="${itemValue2}" path="question[${itemsRow.index}].choice[${indexStatus.index}].${CField.beanName}" />
</c:when>
<c:otherwise>
<c:choose>
<c:when test="${itemSelected}">
<form:checkbox value="${itemValue}" label="${quest3.description}" path="question[${itemsRow.index}].choice[${indexStatus.index}].${CField.beanName}" checked="true" /><br/>
</c:when>
<c:otherwise>
<form:checkbox value="${itemValue}" label="${quest3.description}" path="question[${itemsRow.index}].choice[${indexStatus.index}].${CField.beanName}" /><br/>
</c:otherwise>
</c:choose>
</c:otherwise>
</c:choose>
</c:forEach>
</c:forEach>
</c:when>
<input type="submit" value="Save Section"
class="button-main" />
</fieldset>
</form:form>
</c:forEach>`
The Key bit is in this line
<form:checkbox value="${itemValue}" label="${quest3.description}" path="question[${itemsRow.index}].choice[${indexStatus.index}].${CField.beanName}" checked="true" /><br/>
To link up the command object with its collection for the postback you have to show the indice of the element as part of the spring path. In my case I have two levels of collections to track
<c:forEach items="${quest.qisQuestionsCollection}" var="quest2" varStatus="itemsRow">
varStatus gives you access to a bean object with the index property you can use to your advantage.
In your case you can do just use the index property of the foreach jstl function in the jsp to generate the indice like I did and append it to the array index notation of your command object. The command object must of course follow the same flow as the path collection names. This works for an infinite number of levels but gets more annoying as we go.
This is a large live example so if you need something smaller show me your markup and I will walk you throgh it.

Resources