Is it possible to bind spring form:form to a list element? I've tried this way.
<form:form commandName="products[0]">
<form:input path="name"/>
</form:form>
also
<form:form commandName="products0">
<form:input path="name"/>
</form:form>
Where products list is populated in spring controller.
#RequestMapping(method = RequestMethod.GET)
public String getAll(Map<String, Object> map) {
map.put("products", productService.getAll());
return "products";
}
Received: java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'products[0]' available as request attribute. Which as I understand means that spring haven't found where to bind the form.
No, this is not possible. The value you pass to the commandName attribute is a key and it is not resolved like a normal EL or SpEL expression would be. It is used directly. In other words, with
<form:form commandName="products[0]">
<form:input path="name"/>
</form:form>
Spring will look for a model attribute called products[0] which it won't find.
The alternative is to put the first element of the list in the model directly with a key you will use in your jsp.
Or you can use JSTL, get the first element in the list and create an HTML <form> element yourself.
Related
I have an html page to edit a list of persons.
When the page is opened the controller gets the list of persons from the db and binds to the view.
That works fine.
Then the user edits the data and clicks save.
Now I need to submit the edited data of each person to the controller so that the controller can save the edits.
I'm trying to do that using #ModelAttribute ArrayList<Person> as shown below but it's not working.
The arraylist comes empty.
How do I do for the arraylist to come filled with all of the persons objects from the form?
View
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<form action="editperson" method="post">
<th:block th:each="person : ${personBind}">
Name:
<input type="text" th:value="${person.name}" />
</br></br>
Age:
<input type="text" th:value="${person.age}" />
</br></br>
</th:block>
</br></br>
<input type="submit" name="btnSaveEdit" value="Save"/>
</form>
Controller
#RequestMapping(value = "/editperson", params = "btnSaveEdit", method = RequestMethod.POST)
public String saveEditPerson(#ModelAttribute ArrayList<Person> personsList){
//save edit code here
return "editperson";
}
Spring MVC with Thymeleaf does indeed use model attributes to store data for the Thymeleaf pages. This guide shows how to load data from a form into the application. Try wrapping the ArrayList inside of a custom object and access the list via that object as a ModelAttribute rather than working with an ArrayList directly.
The saveEditPerson method should probably be named "saveEditPersons" since you are parsing a list of persons rather than just one.
In a code that follows Spring MVC, I have 2 lists in my java code that I would like to use in my JSP view. I set them like this:
public ModelAndView circularListView(HttpServletRequest request, Principal principal, HttpSession session, Locale locale, ModelAndView mav, int startOffset) {
//some code
mav.addObject("circularsList", circularsList);
mav.addObject("documentNameList", documentNameList);
return mav;
}
Now I would like to iterate on both lists circularsList and documentNameList in a single for loop in the JSP page, but it seems that I can only set one variable name like this:
<c:forEach items="${circularsList}" var="circular" varStatus="status">
To access a value in the second list which is documentNameList, I do it like this:
<input type="hidden" id="circDocNam" value="${documentNameList[status.index]}"/>
Unfortunately, this does not seem to work, and the value in the above line is empty.
What to do?
In conclusion: How to access a list item using its index in JSP?
This worked for me:
<input type="hidden" id="circDocNam" value="<c:out value="${documentNameList[status.index]}"/>"/>
I have a register.jsp page where I have bind the following data to a modelAttribute userform.
<form:form method="post" modelAttribute="userform"
enctype="multipart/form-data" autocomplete="off" class="form">
<spring:bind path="firstName">
<label>First Name: </label>
<form:input path="firstName" placeholder="First Name" />
</spring:bind>
<spring:bind path="lastName">
<label class="control-label">Last Name: </label>
<form:input path="lastName" placeholder="Last Name" />
</spring:bind>
</form:form>
where get and post methods on the controller are:
#RequestMapping(value = "/register", method = RequestMethod.GET)
public String register(#ModelAttribute("userform") Employee employee, Model model) {
List<Role> roleList = roleService.getAllList();
model.addAttribute("roleList", roleList);
model.addAttribute("userform", employee);
return "employee/register";
}
#RequestMapping(value = { "/register" }, method = RequestMethod.POST)
public String processRegistration(#Valid #ModelAttribute("userform") Employee employee, BindingResult result,
Model model, HttpServletRequest request) throws IllegalStateException, IOException {
//(expecting the data from jsp) nothing in the employeee object :(
//dosomething
return "employee/register";
}
Although I have used same name userform and the attributes name on the entity Employee is exactly the same, I am unable to get the form data from JSP to the Controller. I must be doing something wrong here but could not find it. Any help would be appreciated.
I figured out the answer myself.
Since I have used enctype="multipart/form-data"in my JSP form, it needed a configuration of bean called "CommonsMultipartResolver" in the servlet-context.xml. The bean as a whole can be written as
<beans:bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver" />
In addition to #Ralph answer I want to Explain why you need to remove below lines from your jsp
<spring:bind path="firstName">
<spring:bind path="lastName">
As Per Answer given on this link Difference between spring:bind and form:form
Its Unnecessary to use <spring:bind> when you use <form:form> because both does same thing with respect to model attributes.
here <form:form> also generates HTML form markup, whereas with <spring:bind> you need to write markup yourself.
Also you should know how sping handles binding of Domain Object, As #Ralph mention in his answer You need to use form backing object and modify you method like below
#RequestMapping(value = "/register", method = RequestMethod.GET)
public String register(Employee employee, Model model) {
}
Q- What is happening in above code?
A- When Spring MVC finds out domain object is present as a method argument an instance is automatically introduced by Spring MVC,with respect to domain objects this instance is same as instance created by new keyword like below
Employee emp=new Employee();
The properties of Employee domain object are normally uninitialized accept any parameters with same names as Employee object properties available in the URL query String. Spring MVC uses Java reflection to dinf out names of properties of the domain object.
For more information visit Binding and Validation of Handler Parameters in Spring MVC Controllers
I have been looking for this answer here and in google without any success. I will explain what I´m looking for and let´s see if someone can help me.
Using Spring MCV I´m rendering a List of A objects "aList" into my view.JSP.
#RequestMapping(method = RequestMethod.GET)
public final ModelAndView getAList(){
ModelAndView mav = new ModelAndView("view");
List<A> aList = new ArrayList<>();
aList.add(new A("a1");
aList.add(new A("a2");
mav.addObject("aList", aList);
return mav;
}
Where A looks like
public class A{
#Getter
#Setter
private String value;
public A(String value){
this.value=value;
}
}
For every iteration of the list I´m creating a form. The form looks like
<c:forEach var="a" items="${aList}"
varStatus="status">
<form:form id="A${a.id}"
method="post" action="save.do"
modelAttribute="a">
<input type="submit" value="Save"/>
</form:form>
</c:forEach>
so after finish the render I have so many forms as objects in my list.
Every form as you can imagine has his submit button.
Now what I´m trying to do without any success is to send one of this form to my controller, but not the whole list of A that I render but A. So my controller will look something like.
RequestMapping(value = "/save", method = RequestMethod.POST)
public final ModelAndView save(#ModelAttribute("a") A a) {
But I´m receiving a "IllegalStateException: Neither BindingResult nor plain target object for bean name" because the render name of my modelAndView "aList" is not the same than "a".
What I think I understand is that Spring MVC components like form dont allow submit a different class that was used in the render. Even if is a nested class of the render class, what is too bad.
I would like to avoid have to send the aList again.
Any idea?
Regards.
Instead of attempting to post back the entire contents of the selected element,
consider posting back an identifier for the selected element.
For example:
<form:form method="post" action="save.do">
<c:forEach items="${aList}"
var="element"
varStatus="status">
<input type="submit"
name="selectedElementId"
value="${element.id}"/>
</c:forEach>
</form:form>
Ok, it works. The problem if that the <form:xxx> elements (eg <form:input>) have a problem with a path that starts with an element which is not in the model in the render part.
So I slightly modified the exemple given. Here is the form part :
<ul><c:forEach var="a" items="${aList}" varStatus="status">
<li><form:form modelAttribute="a" action="save.do" method="POST" >
<input type="hidden" name ="index" value="${status.index}">
<input type="text" name ="value" value="${a.value}">
<input type="submit" value="Save"/>
</form:form></li>
</c:forEach></ul>
The <li> are only for minimal formatting, but the important part is the use of direct <input> elements so that Spring MVC don't choke on a not being part of the model. So the rendering works.
Now for the save part :
RequestMapping(value = "/save", method = RequestMethod.POST)
public final String save(#ModelAttribute("a") A a, #RequestParam("index") int index){
A aa = aService.getaList().get(index);
aa.setValue(a.getvalue());
return "redirect:/list";
}
There is no problem, because the name of the model attribute is not present in the POST request. Springs simply creates a new A and sets its value attribute. This solution is extensible for complex A classes. The only problem, is that I do not manage errors returned by Spring. They have to be managed at a global level.
(I assumed the URL for the list part was /list)
The same EL expression ${taskId} gives two different values in different places.
I am using the Stripes framework, along with MongoDB and Morphia for Object-Mapping (and of course Java/JSP/etc).
Using the following JSP/Expression Language code:
<c:forEach items="${actionBean.tasks}" var="listTask">
<c:set var="taskId" scope="page" value="${listTask.id}"/>
<s:form method="post" beanclass="action.TaskActionBean">
${taskId}<s:hidden name="task.id" value="${taskId}"/>
<s:submit name="promoteTask" value="Up" />
</s:form>
</c:forEach>
Gives the following generated code:
<form method="post" action="/scrumyogi/">
4ef99b730364de7ec70dbd68
<input type="hidden" value="4ef99b6c0364de7ec70dbd67" name="task.id">
<input type="submit" value="Up" name="promoteTask">
<div style="display: none;">
<input type="hidden" value="NPNEJw6tUWfRBXf-vVOLTw==" name="_sourcePage">
<input type="hidden" value="XbfUDiSHGrU=" name="__fp">
</div>
</form>
As you can see ${taskId} is printing out 4ef99b730364de7ec70dbd68 and then 4ef99b6c0364de7ec70dbd67, which makes no sense to me, I need ${taskId} to print out the same value 4ef99b730364de7ec70dbd68 is the correct one.
Is there some known issue that could cause this.
EDIT: the real problem is that the ${taskId} within the hidden form tag is incorrect, I printed the other value to see what the expression contains, and then found that it's different in the different locations - which make things seriously confusing.
ActionBean code:
#UrlBinding("/")
public class TaskActionBean extends BaseActionBean{
String taskId;
Task task = new Task();
List<Task> tasks;
public final static String DISPLAY = "/index.jsp";
#DefaultHandler
public Resolution listTasks(){
tasks = Dao.datastore().find(Task.class).order("rank").asList();
return new ForwardResolution(DISPLAY);
}
public Resolution promoteTask(){
task.promoteTask();
tasks = Dao.datastore().find(Task.class).order("rank").asList();
return new ForwardResolution(DISPLAY);
}
// ... getters and setters
You have a taskId field in you action bean, and according to stripes taglib documentation:
The hidden tag assigns the value attribute by scanning in the following order:
for one or more values with the same name in the HttpServletRequest
for a field on the ActionBean with the same name (if a bean instance is present)
by collapsing the body content to a String, if a body is present
referring to the result of the EL expression contained in the value attribute of the tag.
So it probably finds the field in your action bean and takes the value from there.
The other (jsp el) ${taskId} is assigned from task list element.
Change the taskId to some name that doesn't coincide with your action bean field and it should work.