Spring 3 #ModelAttribute with List in model - spring

I have two models :
public class Size {
private String name;
// getter and setter
}
public class Product {
private int id;
private String designation;
private Float price;
private List<Size> availableSizes;
// Getter and setters
}
I have defined Sizes in my servlet, and now I nead to create products with the availables sizes.
What I do in my index Controller:
ModelAndView render = new ModelAndView("admin/index");
render.addObject("products", productFactory.getProducts());
render.addObject("sizes", sizeFactory.getSizes());
render.addObject("command", p);
return render;
I've a list of products, and my list of sizes.
In my index view, I do:
<form:form method="post" action="/ecommerce/admin/products" class="form-horizontal">
<form:input path="id" />
<form:input path="designation" />
<form:input path="price" />
<form:select path="availableSizes" items="${sizes}"/>
<input type="submit" value="Ajouter le produit" class="btn" />
</form:form>
Then, in new product controller, I do :
// To fix: Sizes not saved !
#RequestMapping(value = "/products", method = RequestMethod.POST)
public ModelAndView newProduct(#ModelAttribute("Product") Product product,
BindingResult result) {
productFactory.add(product);
return buildIndexRender(null, null, product);
}
The problem is that I keet the posted product, but not the corresponding sizes.
Is there a way to keep selected sizes in the form int the controler, or directly in the model?
Thanks.

It's a very common problem.
To set a List<Size> in Product instance from the post data, that is a string colon separated list with selected sizes, all you need to do is tell the framework how to convert a size String to a Size instance. The most common approach is registering a PropertyEditor on WebDataBinder
class SizePropertyEditor extends PropertyEditorSupport {
#Override
public void setAsText(String text) throws IllegalArgumentException {
Size size = stringToSize(text); // write this code
setValue(size);
}
}
and register the property editor in Controller using #InitBinder annotation
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Size.class, new SizePropertyEditor());
}
For a more generic approach see ConversionService.

Related

How to bind Spring form:checkbox instead of form:checkboxes?

I am having problems with form:checkbox. I cannot make it display selected values. When I selected values and submit, correct values are display in database. When I load page all values (checkboxes) are not selected.
Elements below are located inside this:
<form:form role="form" commandName="user" class="form-horizontal" action="${form_url}">
</form:form>
This works just fine:
<form:checkboxes items="${availableRoles}" path="roles" itemLabel="role" itemValue="id" element="div class='checkbox'"/>
This doesn't work:
<c:forEach items="${availableRoles}" var="r" varStatus="status">
<div class="checkbox">
<form:checkbox path="roles" label="${r.description}" value="${r.id}"/>
</div>
</c:forEach>
This is my domain class:
public class User {
private List<Role> roles;
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
This is my custom property editor:
public class RolePropertyEditor extends PropertyEditorSupport {
#Override
public void setAsText(String text) {
Role role = new Role();
role.setId(Integer.valueOf(text));
setValue(role);
}
}
Controller has this method:
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Role.class, new RolePropertyEditor());
}
Controller method:
#RequestMapping(value = "/update/{userId}", method = RequestMethod.GET)
public String updateUser(#PathVariable Integer userId, Model model) {
User user = userService.getByUserId(userId);
List<Role> availableRoles = roleService.getAllRoles();
model.addAttribute("availableRoles", availableRoles);
model.addAttribute("user", user);
return "user/update";
}
After debugging session I found the solution.
Because of Spring internals JSP should look like this:
<c:forEach items="${availableRoles}" var="r">
<div class="checkbox">
<form:checkbox path="roles" label="${r.description}" value="${r}" />
</div>
</c:forEach>
Notice that value is item (r), not item's member like r.id.
Also you need getAsText implementation in your custom PropertyEditor.
#Override
public String getAsText() {
Role role = (Role) this.getValue();
return role.getId().toString();
}

Spring receive data from the client

Good evening!
public class Order {
private int idOrder;
private Basket basket;
// getter and setter
}
public class AnonymousOrder {
private String name;
private String telephone;
// getter and setter
}
public class UserOrder {
private User user;
// getter and setter
}
public class OrdersForm {
private List< ? extends Order> orders;
// getter and setter
}
#RequestMapping(value="/showOrders")
public String showOrders(Model model){
List<? extends Order> orders= adminManager.searchAllOrders();
OrdersShowForm ordersForm = new OrdersShowForm();
ordersForm.setOrders(orders);
model.addAttribute("ordersForm", ordersForm);
return "showOrders";
}
#RequestMapping(value="/showOrders", method = RequestMethod.POST)
public String showOrdersPOST(#ModelAttribute("ordersForm") OrdersShowForm ordersForm){
System.out.print(ordersForm);
return "showOrders";
}
<form:form modelAttribute="ordersForm">
<table class="features-table" border="1">
<c:forEach items="${ordersForm.orders}" var="order" varStatus="status">
<tr>
<c:if test="${order['class'].simpleName != 'UserOrder'}">
<td>
<input name="orders[${status.index}].name" value="${order.name}"/>
</td>
</c:if>
</c:forEach>
</table>
Problem: I am passing on page two types of data: UserOrder and AnonymousOrder, but when I try to get them on the server then come data type Order.
Question: How to transfer data to the server without changing their actual type?
P.S. sorry for my English)

How to Set Form Bean before it goes to JSP page

I am an absolute beginner. So please bear with me if I miss the obvious.
Environment I am using: Spring Portlet MVC (3.0), Liferay 6.0.6
I have a controller, a form bean and a JSP page.
I am able to successfully submit a form and get the form bean by using the below code. However I am stuck on how to preload some values into my form bean before the bean is forwarded to JSP. Can somebody point out the right direction:
My Controller:
#ActionMapping(params = "spring_action=resetPasswordViewAction")
protected void resetPasswordAction(ActionRequest actionRequest, Map<String, Object> model, ActionResponse actionResponse, #RequestParam String customerId, #RequestParam String userName) {
model.put("customerId", customerId);//Preload form bean value with this
model.put("userName", userName);//Preload form bean value with this
actionResponse.setRenderParameter("spring_render", "resetPasswordView");
}
#RenderMapping(params = "spring_render=resetPasswordView")
protected ModelAndView resetPasswordView(RenderRequest renderRequest, Map<String, Object> model) {
return new ModelAndView("resetPassword", model);
}
#ActionMapping(params = "spring_action=resetPasswordUpdateAction")
protected void resetPasswordUpdateAction(ActionRequest actionRequest, Map<String, Object> model, ActionResponse actionResponse, final ResetPassword resetPasswordCriteria) {
LOG.info(resetPasswordCriteria.toString());// Form values are retrieved successfully
actionResponse.setRenderParameter("spring_render", "resetPasswordView");
}
#ModelAttribute("resetPasswordCriteria")
public ResetPassword getResetPasswordCriteria() {
return new ResetPassword();
}
My JSP Page:
<form:form id="resetPasswordForm" name="resetPasswordForm" commandName="resetPasswordCriteria" method="post" action="${resetPasswordUpdateActionURL}">
<form:label path="customerId" /><!--Preload this field value-->
<form:label path="userName" /><!--Preload this field value-->
<form:password path="password" />
<form:password path="confirmPassword" />
<input type="submit" value="Submit" />
</form:form>
Form Bean:
public class ResetPassword {
private String customerId = "";
private String userName = "";
private String password = "";
private String confirmPassword = "";
//Getters Setters
}
In your rendering method resetPasswordView, place an object named resetPasswordCriteria (your commandName in jsp) of type ResetPassword to the model.

Form input constraints not being enforced?

I'm new to Tomcat and Spring Web. I'm trying to use Spring's form validation features by following this tutorial. Everything seems to run smoothly except for one thing... my form doesn't do any validation and I can always get to the success page when I send the form no matter which data I provide.
Am I using the constraints correctly? I want to enforce that the user fills in their first name and that the first name be at least two characters long.
package net.devmanuals.form;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.NotEmpty;
public class RegistrationForm {
#NotEmpty(message = "You surely have a name, don't you?")
#Size(min = 2, message = "I'm pretty sure that your name consists of more than one letter.")
private String firstName;
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getFirstName() {
return this.firstName;
}
}
Form code:
<form:form method="post" commandName="regform">
<p><form:input path="firstName" /> <form:errors path="firstName" /></p>
<p><input type="submit" /></p>
</form:form>
The controller:
#Controller
#RequestMapping("/register")
public class RegistrationController {
#RequestMapping(method = RequestMethod.GET)
public String showRegForm(Map model) {
RegistrationForm regForm = new RegistrationForm();
model.put("regform", regForm);
return "regform";
}
#RequestMapping(method = RequestMethod.POST)
public String validateForm(#Valid RegistrationForm regForm, BindingResult result, Map model) {
if (result.hasErrors()) {
return "regform";
}
model.put("regform", regForm);
return "regsuccess";
}
}
Am I applying the constraints incorrectly?
In addition to adding <mvc:annotation-driven/> to your config, you need to make sure the JSR-303 jar is on your classpath. From the docs:
[AnnotationDrivenBeanDefinitionParser] ... configures the validator if specified, otherwise defaults to a fresh Validator instance created by the default LocalValidatorFactoryBean if the JSR-303 API is present on the classpath.

Spring Framework - New object is created in DB instead of updating

I have an application written in Spring 3.0 hooked up using Hibernate to a database. I have a controller to an update form. Whenever the form is submitted, I expect the object that is shown to be updated however a new object is created with a new ID value. I've looked over the "petclinic" sample and i can't see how it is different.
POJO
public class Person
{
private int id;
#NotNull
private String name;
//getter/setter for id
//getter/setter for name
}
Controller
public class PersonUpdateController
{
//injected
private PersonService personService;
#RequestMapping(value="/person/{personId}/form", method=RequestMethod.POST)
public String updateForm(ModelMap modelMap, #PathVariable personId)
{
Person person = personService.getById(personId);
modelMap.addAttribute(person);
return "person/update";
}
#RequestMapping(value="/person/{personId}", method=RequestMethod.POST)
public String update(ModelMap modelMap, #Valid Person person, BindingResult bindingResult)
{
if(bindingResult.hasErrors())
{
modelMap.addAttribute(person);
return "person/update";
}
personService.save(person);
return "redirect:person/" + person.getId() + "/success";
}
}
JSP
<spring:url value="/person/${person.id}" var="action_url" />
<spring:form action="${action_url}" modelAttribute="person" method="POST">
<spring:input name="name" path="name" />
<input type="submit" value="Save" />
</spring:form>
PersonService Implementation
public class HibernatePersonService
implements PersonService
{
//injected
private SessionFactory sessionFactory;
//other methods
public void save(Person person)
{
Session session = sessionFactory.getCurrentSession();
session.saveOrUpdate(person);
}
}
Spring MVC doesn't do any magic with HTML forms. Since your form contains only one field, you get only one field populated in update method. So, you have two options:
Pass id as a hidden field in the form: <spring:hidden path = "id" />. Note that in this case you need to check possible consequences for security (what happens if malicious person changes the id).
Store Person in the session so that data from the form is used to update the stored object (note that it may cause interference if several instances of the form is opened in one session). That's how it's done in Petclinic:
-
#SessionAttributes("person")
public class PersonUpdateController {
...
#RequestMapping(value="/person/{personId}", method=RequestMethod.POST)
public String update(ModelMap modelMap, #Valid Person person,
BindingResult bindingResult, SessionStatus status)
{
...
personService.save(person);
status.setComplete(); // Removes person from the session after successful submit
...
}
#InitBinder
public void setAllowedFields(WebDataBinder dataBinder) {
dataBinder.setDisallowedFields("id"); // For security
}
}

Resources