Java spring post form one to many relationship - spring-mvc

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>

Related

How to use input radio button with thymeleaf and Spring MVC

I would like to get a destination address from a input radio button list. The DestinationAddress class is the following:
public class DestinationAddress {
private Integer destinationAddressId;
private String name;
private Location location;
private User owner;
public DestinationAddress(String name, Location location, User owner) {
this.name = name;
this.location = location;
this.owner = owner;
}
public DestinationAddress() {
}
// getter and setter
}
The controller who handles the get and post is the following:
#PreAuthorize("hasRole('USER')")
#GetMapping(value = "/select-address")
public String selectAddress(Principal principal, Model model) {
List<DestinationAddress> addresses = destinationAddressService.getAllByUsername(principal.getName());
model.addAttribute("destinationAddresses", addresses);
model.addAttribute("destinationAddress", new DestinationAddress());
return "purchase/select-address";
}
#PreAuthorize("hasRole('USER')")
#PostMapping(value = "/select-address")
public String selectAddress(#ModelAttribute DestinationAddress destinationAddress, Principal principal) {
Purchase purchase = purchaseService.addPurchase(principal.getName(), destinationAddress);
return "redirect:/purchases/pay/" + purchase.getPurchaseId();
}
And the html page is the following:
<form th:object="${destinationAddress}" method="post">
<fieldset>
<legend>Your addresses</legend>
<ul>
<li th:each="destinationAddress : ${destinationAddresses}">
<input type="radio" th:field="*{destinationAddressId}" th:value="${destinationAddress.destinationAddressId}" />
<label th:for="${#ids.prev('destinationAddress.destinationAddressId')}" th:text="${destinationAddress}"></label>
</li>
</ul>
</fieldset>
<p><input type="submit" value="Submit" /> <input type="reset" value="Reset" /></p>
</form>
The error message is the following:
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'destinationAddressId' available as request attribute
I don't know what's the problem here. I don't know which type will the form return to the controller. So I don't know which variable pass to the model and which one to get from the post controller method. Integer or DestinationAddress? I cannot find anything googling it, just small pieces of code without any explanations. Any suggestions?
I found a solution to my problem. I changed the html page, now it looks like this:
<form th:object="${address}" method="post">
<fieldset>
<legend>Your addresses</legend>
<ul>
<li th:each="destinationAddress : ${destinationAddresses}">
<input type="radio" th:field="${address.destinationAddressId}" th:value="${destinationAddress.destinationAddressId}" />
<label th:for="${destinationAddress.destinationAddressId}" th:text="${destinationAddress}"></label>
</li>
</ul>
</fieldset>
<p><input type="submit" value="Submit" /> <input type="reset" value="Reset" /></p>
</form>
I changed the name of the object inside the model because it was the same as the name of the temp destinationAddress of the loop. I also replaced '{#ids.prev(' because it was giving me an error:
Cannot obtain previous ID count for ID ...
Now it works fine.

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: <form:select> option won't stay selected

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.

SPRING MVC post method call not working

By JSP has below :
<h2>Student Information</h2>
<form:form method="POST" action="/HelloWeb/addStudent">
<table>
and my java controller code has below
#RequestMapping(value = "/addStudent", method = RequestMethod.POST)
public String addStudent(#ModelAttribute("SpringWeb") Student student,
ModelMap model) {
when i try to hit the post doesnt work i,e /HelloWeb/addStudent,
I tried making both places /HelloWeb/addStudent or just /addStudent that doesnt work.
FYI : HelloWeb here is the DispatchServletName given in web,xml
I am trying example given in site
http://www.tutorialspoint.com/spring/spring_mvc_form_handling_example.htm
I apologize if i am asking very basic easisest issue, BUt tried this # late nite and fed up so requesting ppl to help/suggest
The attribute in jsp require modelAddribute or commandName which is the class instance of the domain object. You did not specify it. So the
<form:form method="POST" modelAttribute="SpringWeb" action="/HelloWeb/addStudent">.
There is a standard way to do form post submission in spring. You need to do GET request mapping to map/bind the Student table with jsp, and POST mapping to submit jsp form data. An example in your case would be.
#RequestMapping(value = "/addStudent", method = RequestMethod.GET)
public String addStudent(#ModelAttribute("SpringWeb") Student student) {
return "addstudentJsp"; // your jsp page name where the spring:form is placed
In jsp page do this
<h2>Student Information</h2>
<form:form modelAttribute="SpringWeb">
<form:input id="name" path="name" type="text" class="form-control" />
<form:errors path="name" cssClass="text-danger"></form:errors>
// your student fields
<button type="submit">submit</button>
</form:form>
Now again in your controller have a post request method like
#RequestMapping(value = "/addStudent", method = RequestMethod.POST)
public String addStudent(#ModelAttribute("SpringWeb") Student student, #BindingResult result) {
//Call to your data persistence layer like StudentService
}
The modelAttribute does the binding for you
I faced same problem, I just rename my project "HelloWeb" and the problem was solved.

Spring 3.1 MVC and Security: both login and registration form (multiple forms) on same page get submitted

I'm using Spring (3.1), Spring MVC (3.1) and Spring Security (3.0) in combination and I've put together a single JSP page that has two forms on it; One is a form to login and the other is a form to register (i.e. create a new user).
For the register form, I use the Spring form tags, backed up by a controller to handle the request but for the login form I don't bother with the Spring form tags as I don't believe they're needed. There is also no controller that I need to write to handle the form submission as Spring Security takes care of authenticating so long as the request is submitted to j_spring_security_check.
The register form is working fine but the login form is a problem. It seems that when I click the submit button on the login form, the registration form is also submitted, or at least Spring thinks I'm trying to submit that form. Here is the JSP:
<form id="loginform" method="POST" action="<c:url value='j_spring_security_check'/>">
<label for="existing_email">Email:</label>
<input name="j_username" id="existing_email" type="text" value="${SPRING_SECURITY_LAST_USERNAME}" />
<label for="existing_password">Password:</label>
<input name="j_password" id="existing_password" type="password" />
<input id="login-form-submit" type="submit" value="Sign in" />
</form>
<form:form id="registrationform" modelAttribute="user" method="POST" action="register">
<form:label path="username" for="email">Email:</form:label>
<form:input path="username" name="username" id="email" type="text" />
<form:errors path="username" cssClass="formError" />
<form:label path="password" for="password">Password:</form:label>
<form:input path="password" name="password" id="password" type="password" />
<form:errors path="password" cssClass="formError" />
<input id="registration-form-submit" type="submit" value="Sign up" />
</form:form>
Notice that form tags for the input of type submit are not present and this seems to be a normal thing to do in the examples I've seen. Adding form tags to the submit button I guess doesn't make sense as it doesn't map to anything on the target object (user in this case).
When I click the "Sign in" button I get the following exception:
SEVERE: Servlet.service() for servlet [appServlet] in context with path [/project1] threw exception [An exception occurred processing JSP page /WEB-INF/views/registration.jsp at line 29
28: <form:form id="registrationform" modelAttribute="user" method="POST" action="register">
29: <form:label path="username" for="username">Username:</form:label>
30: <form:input path="username" name="username" id="username" type="text" />
31: <form:errors path="username" cssClass="formError" />
32:
Stacktrace:] with root cause
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'user' available as request attribute
at org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:141)
This I recognise from cases where you forget to include the modelAttribute attribute in form:form, but of course I don't want to submit this form to my controller.
I have a feeling there is a mistake I'm making or a simple solution. Can anyone recommend a way around this or perhaps a different approach?
Here is the controller method that handles requests to register in case that's needed:
#RequestMapping(value = "**/register", method = RequestMethod.POST)
public String registerUser(#ModelAttribute("user") #Validated User user, BindingResult errors, ModelMap model) {
if (errors.hasErrors()) {
return "registration";
}
// Other stuff then...
return "profile"
}
If you are using "user" modelAttribute in form tag then a non-null request attribute must be present with name "user".
One way to add that in request attribute is what you did in your answer above. Other ways are:
(1) Add in ModelMap:
#RequestMapping(value = "/loginfailed", method = RequestMethod.GET)
public String loginFailed(ModelMap model) {
model.addAttribute("user", new User());
model.addAttribute("error", "true");
return "registration";
}
(2) Add in request scope (Using WebRequest or HttpServletRequest):
#RequestMapping(value = "/loginfailed", method = RequestMethod.GET)
public String loginFailed(ModelMap model, WebRequest webRequest) {
webRequest.setAttribute("user", new User(), WebRequest.SCOPE_REQUEST);
model.addAttribute("error", "true");
return "registration";
}
(3) Use #ModelAttribute on method:
#ModelAttribute("user")
public User user() {
return new User();
}
Please also see Using #ModelAttribute on a method and Using #ModelAttribute on a method argument
Also note that you don't have to use type attribute. See form:input and form:password.
I think the problem is specifically when a login fails and the same page is served up, albeit on a different URL path and so through a different controller method. Therefore my original suspicion that the issue is that both forms are submitted may be something of a red herring, though I still don't fully understand what's going on and that may yet have something to do with it. In any case, this is what corrected the problem for me:
I had a controller method that originally looked like this:
#RequestMapping(value = "/loginfailed", method = RequestMethod.GET)
public String loginFailed(ModelMap model) {
model.addAttribute("error", "true");
return "registration";
}
In the Spring Security context I specify /loginfailed as the path to go to by default if a login attempt fails. This is where it seems the user object is needed so if I alter the signature as follows it all works:
#RequestMapping(value = "/loginfailed", method = RequestMethod.GET)
public String loginFailed(#ModelAttribute("user") User user, BindingResult errors, ModelMap model) {
model.addAttribute("error", "true");
return "registration";
}
Any comments/clarification welcome from others.

Resources