InitBinder on multiple form objects - spring-mvc

Can you have a #Initbinder on several data in the same form?
I have a spring form which contains a select dropdown of an object and two datafields, I have an Initbinder on the dates otherwise I get an error on submit. But I also need to bind the dropdown to an object.
I have a Type which has two dates and a Category, and it is the Category I need to bind because it can not be empty on save.
I think it will help me to validate the form to.
So can I have this in my Type controller?
#InitBinder
public void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
binder.registerCustomEditor(Category.class, "category", new CategoryEditor(CategoryService));
}
And this is the Editor:
public class CategoryEditor extends PropertyEditorSupport {
private CategoryService categoryService;
public CategoryEditor(CategoryService categoryService) {
this.categoryService = categoryService;
}
#Override
public void setAsText(String text) throws IllegalArgumentException {
if (text.equals("0")) {
this.setValue(null);
} else {
Category sc = categoryService.getCategory(Integer.parseInt(text));
this.setValue(sc);
}
}
#Override
public String getAsText() {
Category parent = new Category();
if (this.getValue() != null) {
parent = (Category) this.getValue();
}
return "";
}
}
And my jsp-page
<s:url value="/mvc/type/save" var="actionUrl" />
<sf:form method="POST" modelAttribute="type" action="${actionUrl}">
<fieldset>
<legend><s:message code="${heading}" /></legend>
<table>
<tr>
<th><label for="category"><s:message code="category" />:</label></th>
<td><sf:select path="category.ID" id="category">
<sf:option value="0"> </sf:option>
<sf:options items="${listOfCategories}" itemLabel="name" itemValue="ID" />
</sf:select></td>
</tr>
<tr>
<th><label for="name"><s:message code="name" />:</label></th>
<td><sf:input path="name" id="name" />
<sf:hidden path="ID" />
<sf:hidden path="version" /></td>
</tr>
<tr>
<th><label for="marketing"><s:message code="marketing" />:</label></th>
<td><sf:input path="marketingFunction" id="marketing" /></td>
</tr>
<tr>
<th><label for="status"><s:message code="status" />:</label></th>
<td><sf:select path="lifeCycleStatus">
<sf:option value="0"> </sf:option>
<sf:options items="${listOfEnums}" />
</sf:select></td>
</tr>
<tr>
<th><label for="validfrom"><s:message code="validfrom" />:</label></th>
<td><sf:input path="validFrom" id="validfrom" /></td>
</tr>
<tr>
<th><label for="validuntil"><s:message code="validuntil" />:</label></th>
<td><sf:input path="validUntil" d="validuntil" /></td>
</tr>
<tr>
<td colspan="2">
<input id="saveButton" class="right" type="submit" title="<s:message code="save" />" value=" [ <s:message code="save" /> ] " />
</td>
</tr>
</table>
</fieldset>
</sf:form>
So my question: Can I have multiple binder in the same initBinder in my controller? It seems like I can't because I never enter the CategoryEditor. How do I do this?

There aren't multiple binders, there are multiple PropertyEditors.
Your custom one is never called because you are binding the wrong path.
<sf:select path="category.ID" id="category">
You must bind to category and not category.ID
<sf:select path="category" id="category">

Related

Spring #ModelAttribute not working

I have a controller in which I put both the get and post methods. Both methods work fine but when I introduce #ModelAttribute annotation to the POST method, it starts giving me Status 400 -- The request sent by the client was syntactically incorrect.
I do not know what I am doing wrong. The View looks like:
<form:form method="post" action="createIdeaPublic" commandName="idea">
<table class="form">
<tr>
<td align="right"><label for="title">Idea Title</label></td><td align="left"><form:input type="text" path="title" id="title"></form:input></td>
<td align="right"><label for="email">Your Email</label></td><td align="left"><form:input type="text" path="requestorEmail" id="requestorEmail" class="emails"></form:input></td>
</tr>
<tr>
<td align="right"><label for="partnerEmail">CI Contact Email</label></td><td align="left"><form:input type="text" path="cIInitialContact" id="cIInitialContact" class="emails"></form:input></td>
<td align="right"><label for="sponsorEmail">Sponsor Email</label></td><td align="left"><form:input type="text" path="sponsorEmail" id="sponsorEmail" class="emails"></form:input></td>
</tr>
<tr>
<td align="right"><label for="requestedDeliveryDate">Requested Delivery Date</label></td><td align="left"><form:input type="text" path="requestedDeliveryDate" id="requestedDeliveryDate" class="datepicker"></form:input></td>
<td align="right"><label>Classification</label></td><td align="left">
<label for="discretionary" class="radio">Discretionary</label>
<form:radiobutton path="stateDescription" id="discretionary" value="Discretionary"></form:radiobutton>
<label for="mandatory" class="radio">Mandatory</label>
<form:radiobutton path="stateDescription" id="mandatory" value="Mandatory"></form:radiobutton>
<label for="regulatory" class="radio">Regulatory</label>
<form:radiobutton path="stateDescription" id="regulatory" value="Regulatory"></form:radiobutton>
</td>
</tr>
<tr>
<td colspan="4" align="right"><input type="submit" class="ui ui-button ui-corner-all ui-widget" style="margin-top: .6em; margin-right: 1em;font-weight: bold;font-size: 1.2em; width: 150px;" value="Create Idea" /></td>
</tr>
</table>
</form:form>
I tried changing the commandName="idea" to modelAttribute="idea" but no benifit.
The Spring controller looks like
#Controller
#RequestMapping ("/createIdeaPublic")
public class CreateIdeaPublicController{
#RequestMapping(method = RequestMethod.GET)
public ModelAndView view(ModelMap model) {
model.addAttribute("areas",Utils.areas);
return new ModelAndView("createIdeaPublic", "idea", new Idea());
}
#RequestMapping(method = RequestMethod.POST)
public String submit(#ModelAttribute("idea")Idea idea, ModelMap model) {
// System.out.println(idea.getTitle());
System.out.println("Hello World");
return "redirect:createIdeaPublic";
}
}
But as soon as I remove the #ModelAttribute("idea")Idea idea, from the submit method, the form submission starts working.
I figured it out. It was issue with the date field. If I dont enter a value into the date field, I would get 400 page. So here is what I did in the Controller class. Copy as it is if you have this issue (Just change the date format accordingly).
#InitBinder
public void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");
dateFormat.setLenient(false);
// true passed to CustomDateEditor constructor means convert empty String to null
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
}

Spring 3.2 MVC -- validation errors not appearing

I am trying to run a validator class for my Spring form, but my errors are not showing up on my page. I'm a newbie when it comes to Spring validation, so there are undoubtedly some errors here. Here is my form. (Incidentally, Eclipse is posting warnings on the form:error lines that say, "List is a raw type. References to generic type List should be parameterized.")
<form:form commandName="bulletin" method="post" action="processBulletin">
<table>
<tr>
<td>Name:</td>
<td><form:errors path="name" /></td>
<td><form:input path="name" maxlength="30" /></td>
</tr>
<tr>
<td>Subject:</td>
<td><form:errors path="subject" /></td>
<td><form:input path="subject" maxlength="50" /></td>
</tr>
<tr>
<td valign="top">Message:</td>
<td><form:errors path="note" /></td>
<td><form:textarea path="note" cols="70" rows="20" /></td>
</tr>
<tr>
<td><input type="submit" /></td>
<td> </td>
</tr>
</table>
</form:form>
Here is my controller class. I'm calling the validation class from here, and I'm not sure if that's the right thing to do, so feel free to say so if it's not.
#RequestMapping(value = "/processBulletin", method = RequestMethod.POST)
public String processBulletin(
#ModelAttribute("bulletin") Bulletin bulletin, BindingResult result) {
final BindException errors = new BindException(bulletin, "bulletin");
bulletinValidator.validate(bulletin, errors);
if (errors.hasErrors()) {
return "redirect:/approvedBulletins";
} else {
// rest of method
}
return "redirect:/approvedBulletins";
}
Here is my validation class.
#Override
public boolean supports(Class<?> cls) {
return Bulletin.class.isAssignableFrom(cls);
}
#Override
public void validate(Object target, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "subject", "Subject is required");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "Name is required");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "note", "Message is required");
}
}
Try to return view name instead of redirect return "redirect:/approvedBulletins"; when you have errors.

Spring Form hidden value is lost on submit

On my 'update user' page I have a hidden field to hold the ID of the current user being edited. I've checked the HTML source and the value populates correctly. However when I submit the form, the userID value ALWAYS comes through as 0 while every other field comes through correctly.
-I've tried setting the type as form:input and submitting it, still 0 on the controller side.
-There are no bindingresult errors.
-Page URL: /admin/update-user.html?uid=3 (I've tried changing the URL variable from uid to userID, no difference.)
So what the heck is going on?! Am I missing something obvious??
JSTL
<form:form commandName="user" method="POST">
<form:hidden path="userID"/>
<table width="400" cellpadding="3" cellspacing="0" border="0" class="datatable">
<tr>
<td>User ID #</td>
<td>${user.userID}</td>
</tr>
<tr>
<td valign="top">Password</td>
<td>****<br />Change Password</td>
</tr>
<tr>
<td>First Name</td>
<td><form:input path="firstName" /></td>
</tr>
<tr>
<td>Last Name</td>
<td><form:input path="lastName" /></td>
</tr>
<tr>
<td>Gender: </td>
<td>
<form:select path="gender">
<form:option value="Male" label="Male" />
<form:option value="Female" label="Female" />
</form:select>
</td>
</tr>
<tr>
<td>Birthday: </td>
<td><form:input path="birthday" id="datepickerpast" readonly="true" /></td>
</tr>
<tr>
<td>Email</td>
<td><form:input path="email" /></td>
</tr>
<tr>
<td> </td>
<td><input type="submit" value="Update" /> <input type="reset"></td>
</tr>
</table>
</form:form>
Controller
#RequestMapping(value = "admin/update-user", method = RequestMethod.POST)
public String processUpdateUser(ModelMap model, #ModelAttribute("user") User user, BindingResult result) throws SQLException {
if (result.hasErrors()) {
model.addAttribute("user", user);
model.addAttribute("pagetitle", "Admin - Update User Details");
return "admin/users/update-user";
} else {
userDao.updateUser(user);
return "redirect:/admin/user-management.html";
}
}
Object Properties with appropriate getters/setters
private int userID;
private String firstName;
private String lastName;
private String username;
private String email;
private Date birthday;
private String gender;
Realized my getter was type Integer but my setter and property was type int. Made all of them Integer and it worked. If someone can explain why there was no error on compile or why it didn't work the way it was, that'd be greatly appreciated.
Sigh, there goes 3 hours of my life.
You got a compiler error because of autoboxing, which allows for the automatic conversion of primitives to their wrapper class alternatives. As to why it didn't work when the getter was using Integer, I can't help you there.

unable to submit form data using form:input

I'm using Spring 3 MVC(3.0.1 Release). I have a bean class Customer and a CustomerForm class as shown below.
public class Contact {
private String firstname;
private String lastname;
private String email;
private String phone;
//getters and setters
}
public class ContactForm {
private List<Contact> contacts;
//getters and setters
}
From Spring Controller I'm populating the contacts and setting it to Model as shown below
private static List<Contact> contacts = new ArrayList<Contact>();
static {
contacts.add(new Contact("Barack", "Obama", "barack.o#whitehouse.com", "147-852-965"));
contacts.add(new Contact("George", "Bush", "george.b#whitehouse.com", "785-985-652"));
contacts.add(new Contact("Bill", "Clinton", "bill.c#whitehouse.com", "236-587-412"));
contacts.add(new Contact("Ronald", "Reagan", "ronald.r#whitehouse.com", "369-852-452"));
}
#RequestMapping(value = "/get", method = RequestMethod.GET)
public ModelAndView get() {
ContactForm contactForm = new ContactForm();
contactForm.setContacts(contacts);
return new ModelAndView("add_contact" , "contactForm", contactForm);
}
In JSP I'm displaying it as editable form as below,
<form:form method="post" action="save.html" modelAttribute="contactForm">
<table>
<tr>
<th>No.</th>
<th>Name</th>
<th>Lastname</th>
<th>Email</th>
<th>Phone</th>
</tr>
<c:forEach items="${contactForm.contacts}" var="contact" varStatus="status">
<tr>
<td align="center">${status.count}</td>
<td><form:input path="contacts[${status.index}].firstname"/></td>
<td><form:input path="contacts[${status.index}].lastname"/></td>
<td><form:input path="contacts[${status.index}].email" /></td>
<td><form:input path="contacts[${status.index}].phone"/></td>
</tr>
</c:forEach>
</table>
<br/>
<input type="submit" value="Save" />
</form:form>
If i'm making modification to the contacts and submiting it the values are not set to the model. But instead of using Spring JSTL if i use plain HTML input tags as shown below the values are getting set to model.
<c:forEach items="${contactForm.contacts}" var="contact" varStatus="status">
<tr>
<td align="center">${status.count}</td>
<td><input name="contacts[${status.index}].firstname" value="${contact.firstname}"/></td>
<td><input name="contacts[${status.index}].lastname" value="${contact.lastname}"/></td>
<td><input name="contacts[${status.index}].email" value="${contact.email}"/></td>
<td><input name="contacts[${status.index}].phone" value="${contact.phone}"/></td>
</tr>
</c:forEach>
When I checked the rendered JSP's HTML source from the browser, I could find a difference in the input control name's as below
//JSP
<form:input path="contacts[${status.index}].firstname"/>
//corresponding HTML not working
<input id="contacts0.firstname" name="contacts0.firstname" type="text" value="Barack"/>
//JSP
<input name="contacts[${status.index}].firstname" value="${contact.firstname}"/>
//corresponding HTML working!
<input name="contacts[0].firstname" value="Barack"/>
Is this a known issue or is there something I'm missing?
Regards,
Makesh.
You can directly use name attribute in the form:input tag like below,it may work.
<td><form:input name="contacts[${status.index}].firstname" path="contacts[${status.index}].firstname"/></td>

How to do the Hibernate validation on the nested list objects?

I need to validate the objects which are stored in the list on my form bean object.
Below is my form bean object.
public class Role implements java.io.Serializable {
// Fields
private int roleId;
#NotBlank
private String roleName;
private boolean active;
#Valid
private List<Module> modules;
// getters anfd setters
}
and below is my object which is present in the list of my main form bean object
public class Module implements Serializable {
private int id;
#NotBlank
private String moduleName;
// other properties and getters and setters
}
Below is my properties file
# -- Role form --
NotBlank.role.roleName=Role Name can not be blank.
NotBlank.module.moduleName=Module Name can not be blank.
Below is My JSP page, the form consists of a role name and modules which can be added to the role.
<table border="0" class="section_tbl2">
<tr>
<td width="150px" valign="top">
<spring:message code="dmx.role.form.label.name"/>
</td>
<td width="10px">:</td>
<td>
<form:input class="txtinput" id="roleName" path="roleName" maxlength="50"/> <form:errors path="roleName" cssClass="error"/>
</td>
</tr>
<tr><td colspan="3" height="8px"></td></tr>
<tr>
<td width="150px" vAlign="top">
Modules
</td>
<td width="10px" vAlign="top">:</td>
<td>
<table>
<tr>
<td>
<input type="button" value="<spring:message code="dmx.role.form.button.addModule.label"/>" onclick="return addModuleRow();"></input>
</td>
</tr>
<tr><td> </td></tr>
</table>
<table cellpadding="0" cellspacing="0" border="0" class="tblstyle1" id="moduleTable">
<thead>
<tr>
<th class="fst" width="200px">
<spring:message code="dmx.role.form.label.moduleName"/>
</th>
<th width="50px"><spring:message code="dmx.role.form.label.create"/></th>
<th width="50px"><spring:message code="dmx.role.form.label.update"/></th>
<th width="50px"><spring:message code="dmx.role.form.label.delete"/></th>
<th width="30px"></th>
</tr>
</thead>
<tbody id="moduleTBody">
<c:forEach items="${role.modules}" var="module" varStatus="status" >
<c:set var="moduleCounter" value="${status.index}"/>
<tr id="moduleRowId_${moduleCounter}">
<td class="fst txt-center">
<form:select onchange="checkIfThisModuleAlreadySelected(this);" class="seloption" id="selectedModule_${moduleCounter}" path="modules[${moduleCounter}].id">
<form:option value="" label="-- Select Module --"/>
<form:options items="${moduleList}" itemLabel="moduleName" itemValue="id" />
</form:select>
</td>
<td class="txt-center">
<form:checkbox id="create_${moduleCounter}" path="modules[${moduleCounter}].create"/>
</td>
<td class="txt-center">
<form:checkbox id="update_${moduleCounter}" path="modules[${moduleCounter}].update"/>
</td>
<td class="txt-center">
<form:checkbox id="delete_${moduleCounter}" path="modules[${moduleCounter}].delete"/>
<td class="txt-center">
<input class="delbtn" id="moduleDelBtn_${moduleCounter}" name="moduleDelBtn[${moduleCounter}]" type="button" onclick="delModuleRow(${moduleCounter});">
</td>
</tr>
</c:forEach>
</tbody>
</table>
</td>
</tr>
<tr><td colspan="3" height="3px"></td></tr>
</table>
I can successfully validate the role name i.e. when role name is blank I get an error message but when module is not selected i do not get any error message.
Please help
Adding #NotNull and #Size constraints to your module list should help:
#Valid
#NotNull
#Size(min = 1)
private List<Module> modules;
The #Valid annotation causes the elements of the annotated collection to be validated but it doesn't validate wether that collection is not null or contains any elements.

Resources