Spring 3 binding object to list - spring

I think my syntax is correct, but the list in my form backing object is not getting populated. It looks like the list itself is being made, just that it has no elements. The other attributes are being populated as expected. Any ideas?
JSP:
<form:form method="post" commandName="addReminder">
Reminder Name <input type='text' name='reminderName' placeholder="Reminder Name"> <br />
Date <input type='text' name='date' placeholder="1/05/2013"> <br />
Time <input type='text' name='time' placeholder="4:00 PM"> <br />
Time Zone <input type='text' name='timeZone' placeholder="EDT"> <br />
<br />
Contacts <input type='text' path="contacts[0].phoneNumber" placeholder="Name"> <br />
<input type='text' path="contacts[1].phoneNumber" placeholder="Name"> <br />
<input type="submit" value = "Add Reminder">
</form:form>
Controller:
#RequestMapping(value = "/AddAReminder", method = RequestMethod.POST)
public String addReminder(#ModelAttribute("addReminder") AddReminder reminder, BindingResult result)
{
//does stuff with the data from the form backing object
return "Add A Reminder";
}
Form backing object:
public class AddReminder
{
private String reminderName;
private String date;
private String time;
private String timeZone;
private ArrayList<Contact> contacts = new ArrayList<Contact>();
private String sentFrom;
private String message;
private String provider;
//getters and setters
Contact object:
public class Contact
{
private String firstName;
private String lastName;
private String phoneNumber;
private String provider;
//getters and setters

you can use <c:forEach> as :
<form:form method="post" commandName="allProductEdit">
<c:forEach items="${allProductEdit.products}" var="prod" varStatus="pStatus">
<form:input path="products[${pStatus.index}].description" />
<form:input path="products[${pStatus.index}].price" />
</c:forEach>
<input type="submit" value="Execute">
</form:form>
check this forum link http://forum.springsource.org/showthread.php?54509-lt-form-input-gt-inside-lt-c-forEach-gt

Ok I figured out what change I made that got this working. I initialize the list with a Contact object on the GET request. I say initalize, but I mean I just add an element to the list so it isn't empty. The list itself is already initialized in the AddReminder class. Spring will then auto grow the list.

Related

Pass a list of user defined object from input field using spring form handling

I have an Employee class that looks like this.
public class Employee {
private String Name;
private List<Address> address;
*****Getters and Setters****
}
And my Address class looks like this
public class Address {
private int addressid;
private Employee employee;
#NotNull(message="Field Cannot be Empty")
private String description;
*****Getters and Setters****
}
I want to bind List of addresses (Employee can have more than one address - 1:M) to the employee class with the data that is parsed through the form. Which looks like this.
I have 3 input address fields, one of the sample input fields look like this...
<div class="form-group">
<div class="row">
<label class="col-sm-3" for="exampleInputEmail1">Address
1</label>
<div class="col-sm-7">
<form:input class="form-control" placeholder="" path="" />
<form:errors path="" cssClass="error" />
</div>
</div>
</div>
Note that I have cut down many unnecessary form fields to demonstrate the problem more clearly. Please help me bind the list of input fields with the relevant class. Any advice for this design is also welcome.
You can use Thymeleaf for this as below.
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>form|data</title>
</head>
<body>
<div class="starter-template">
<form th:action="#{employeeData}" method="post" th:object="${employee}">
<input th:field="*{name}" type="text" th:name="name" name="name" placeholder="name" class="input u-one-third js-get-form-data"/>
<th:block th:each="add,addStat:*{address}">
<input th:field="*{address[__${addStat.index}__].description}" type="text" th:name="add.description" name="description" placeholder="description" class="input u-one-third js-get-form-data"/>
</th:block>
<input type="submit" value="SEND"/>
</form>
</div>
</body>
</html>
Controller.
#RequestMapping(value = "/formData", method = RequestMethod.GET)
public String formData(Map<String, Object> model) {
Employee employee = new Employee();
Address address = new Address();
address.setDescription("TEST");
address.setEmployee(employee);
employee.getAddress().add(address);
model.put("employee",employee);
return "formData";
}
#RequestMapping(value = "/employeeData", method = RequestMethod.POST)
public void employeeData(#Valid Employee employeeData, BindingResult errors) {
System.out.println(employeeData.getName());
}
Please find the working commit here.

how to get form values to controller in spring mvc

I am trying to get form values from jsp to controller in spring mvc, but i am not able to get form data.
This is my DTO (bean)
public class LoginDTO implements Serializable {
private Long id;
private String username;
private String password;
// setter and getter methods
}
and my Jsp
<form class="form-signin" action="test" method="get" modelAttribute="userFormData">
<input type="text" class="form-control"
placeholder="Email" required autofocus>
<input type="password" class="form-control"
placeholder="Password" required>
<input class="btn btn-md btn-success btn-block"
type="submit" value="Signin">
</form>
and my controller
#RequestMapping(value = "/test", method = RequestMethod.GET)
public String checkLogin(#ModelAttribute("userFormData") LoginDTO formData, BindingResult
result) {
System.out.println("Controller...");
System.out.println("=====> " + formData.getUsername());
System.out.println("=====> " + formData.getPassword());
}
Add names to the controls on your JSP pages.
<input type="text" name="username" ...>
<input type="password" name="password" ...>
To let spring understand which form control value should go to which property of the LoginDTO
we can also use the springframework has given us a form tags.so that we can also use that but in that case you have to define your the input path same as the member varibale given in your class.
like this
<form:form method="post" modelAttribute="userFormData">
<form:input path="username" />
<form:input path="password" />
Then in the controller you can write like this as you have written
public String checkLogin(#ModelAttribute("userFormData") LoginDTO formData, BindingResult
result)
In case you want to get the result on other jsp page as well as on console then do:
public String checkLogin(#ModelAttribute("userFormData") LoginDTO formData, BindingResult
result , Model model){
System.out.println("=====> " + formData.getUsername()); //this outputs username on console
System.out.println("=====> " + formData.getPassword()); //this outputs password on console
model.addAttribute("LoginDTO ", LoginDTO );
return "success"; //this is the return page where the username and password will be rendered as view
}

Spring MVC validation errors are not displayed in Freemarker template

I'm trying to display validation errors in a 'user registration' page built with freemarker template if a controller returns binding errors.
My controller's code is as follows:
#Controller
#RequestMapping("/")
public class UserController {
#Autowired
private UserService userService;
#Autowired
private SecurityService securityService;
#Autowired
private UserValidator userValidator;
#RequestMapping(value = "/registration", method = RequestMethod.GET)
public String registration(Model model) {
model.addAttribute("userForm", new User());
return "registration";
}
#RequestMapping(value = "/registration", method = RequestMethod.POST)
public String registration(#ModelAttribute("useraccount") User userForm, BindingResult bindingResult, Model model) {
userValidator.validate(userForm, bindingResult);
if (bindingResult.hasErrors()) {
return "registration";
}
userService.save(userForm);
securityService.autologin(userForm.getUsername(), userForm.getPasswordConfirm());
return "redirect:/explore";
}
while this is the registration.ftl freemarker template I am trying to build :
<div>
<fieldset>
<h1>Create your Account</h1>
<form id="regForm" class="idealform" action="registration" method="post" name='useraccount'>
Username: <input type="text" name="username" /> <errors path="username" cssClass="error"/><br/>
Password: <input type="text" name="password" /><errors path="password" cssClass="error"/><br/>
<label class="main-label" style="width: 91px;"> </label>
<input type="submit" value="submit">
</form>
</fieldset>
I tried also the solution recommended here:
Displaying Spring MVC validation errors in Freemarker templates
and the registration.ftl becomes:
<#assign form=JspTaglibs["http://www.springframework.org/tags/form"] />
<#macro formErrors>
<#assign formErrors><#form.errors path="*" /></#assign>
<#if formErrors?has_content>
<div id="errors">
<#spring.message "admin.error.globalMessage" />
</div>
</#if>
</#macro>
<div>
<fieldset>
<h1>Create your Account</h1>
<#form.form id="regForm" class="idealform" action="registration" method="post" name='useraccount'>
Username: <input type="text" name="username" path="username" /> <br/>
Password: <input type="text" name="password" path="password" /><br/>
<#formErrors />
<label class="main-label" style="width: 91px;"> </label>
<input type="submit" value="submit">
</#form.form>
</fieldset>
</div>
but still the validation messages are not displayed.
Could you share your thoughts with me on this issue?
Thank you very much.
I rewrote your controller code to something like this:
#Controller
#RequestMapping("/")
public class UserController {
#Autowired
private UserService userService;
#Autowired
private SecurityService securityService;
#Autowired
private UserValidator userValidator;
#RequestMapping(value = "/registration", method = RequestMethod.GET)
public String registration(#ModelAttribute(name = "userForm") User user) {
return "registration";
}
#RequestMapping(value = "/registration", method = RequestMethod.POST)
public String registration(#ModelAttribute(name = "userForm") User user, BindingResult result) {
userValidator.validate(user, result);
if (result.hasErrors()) {
return "registration";
}
userService.save(user);
securityService.autologin(user.getUsername(), user.getPasswordConfirm());
return "redirect:/explore";
}
}
1) There is no need to use model.addAttribute("modelName", model) if you use a constructor without arguments, instead you can use a #ModelAttribute annotation specifying the name attribute (by default the name goes from the class name). You only have to be sure that this model is in a consistent state. Also you need to pass the name exactly the same as you use in your view (freemarker template).
Now "registration.ftl"
...
<#import "/spring.ftl" as spring/>
...
<fieldset>
<h1>Create your Account</h1>
<form id="regForm" class="idealform" action="<#spring.url '/registration'/>" method="post">
<#spring.bind 'userForm.username'/>
Username: <input type="text" name="${spring.status.expression}" value="${spring.status.value?html}"/>
<#list spring.status.errorMessages as error>
<span class="error">${error}</span>
<br>
</#list>
<#spring.bind 'userForm.password'/>
Password: <input type="password" name="${spring.status.expression}" value="${spring.status.value?html}"/>
<#list spring.status.errorMessages as error>
<span class="error">${error}</span>
<br>
</#list>
<label class="main-label" style="width: 91px;"> </label>
<input type="submit" value="submit">
</form>
</fieldset>
...
1)You need <#import "/spring.ftl" as spring/> in order to add spring's user-defined directives that are pretty useful.
2)Use <#spring.bind 'userForm.username'> directive to bind following input to your model. Here "userForm" is your model and "username" is a field that you want to bind. This directive also declares new variable "spring.status" that contains an "expression" variable - for a path, a "value" - to populate the form in case it's returned with errors, and "errorMessages" from BindingResult.
3)If you use a message source to support different languages you should change <span class="error">${error}</span> to something like <#spring.message '${error}'/> otherwise you'll get just message codes.
Hope it helps.

Spring - form with 2 modelAttributes

I know how to use a form with 1 modelAttribute but how should I set 2 model attributes in same form ?
I have:
<sf:form id="details" action="/updateuser" method="POST" modelAttribute="updateuser">
<sf:input type="text" name="firstName" path="firstName"/></br>
<sf:input type="text" name="lastName" path="lastName" /></br>
<sf:input type="text" name="email" path="email" /><sf:errors path="email" cssClass="error"></sf:errors> </br>
<sf:input type="text" name="facebook" path="facebook" /> </br>
<sf:input type="text" name="twitter" path="twitter" /> </br>
</sf:form>
firstName lastName and email are for modelAttribute="updateuser" but I need another modelAttribute for facebook and twitter
How should I implement this ?
Thanks!
EDIT:
Isn't there any other way to do this without a wrapper class ? For more info, I have:
class User {
private String firstName;
private String lastName;
private String email;
}
class SocialMedia {
private String facebook;
private String twitter;
}
You can't attach more than one object to the <form>. However, you can create an explicit model class for the form and attach it.
class UpdateUserFormModel {
private Updateuser updateuser;
private Facebook facebook;
private Twitter twitter;
}
You can access nested objects using . operator
<sf:input type="text" name="firstName" path="updateuser.firstName"/></br>
<sf:input type="text" name="facebook" path="facebook" /> </br>
No you can't have more than one model attribute for one form, but you can use a wrapper to wrap your models.
class Wrapper {
private Class1 c1;
private Class2 c2;
....
//getter&setters
}
However, you are allowed to bind several objects to the same model using
model.addObject("objectName",object);
and access the objects in your page by
${objectName.name }

Spring-mvc 3.0 crud with checkboxes issue

I am doing an simple user crud.
My ApplicationUser has the following properties:
private Long id;
private String password;
private String username;
private Collection<Authority> myAuthorities;
private boolean isAccountNonExpired;
private boolean isAccountNonLocked;
private boolean isCredentialsNonExpired;
private boolean isEnabled;
Authority class has:
private Long id;
private String authority;
private String name;
In my jsp, my view has the following form:
<form:form modelAttribute="applicationUser"
action="add" method="post">
<fieldset>
<form:hidden path="id" />
<legend><fmt:message key="user.form.legend" /></legend>
<p><form:label for="username" path="username" cssErrorClass="error"><fmt:message key="user.form.username" /></form:label><br />
<form:input path="username" /> <form:errors path="username" /></p>
<p><form:label for="password" path="password"
cssErrorClass="error"><fmt:message key="user.form.password" /></form:label><br />
<form:password path="password" /> <form:errors path="password" /></p>
<p><form:label for="password" path="password"
cssErrorClass="error"><fmt:message key="user.form.password2" /></form:label><br />
<form:password path="password" /> <form:errors path="password" /></p>
<p><form:label for="myAuthorities" path="myAuthorities"
cssErrorClass="error"><fmt:message key="user.form.autorities" /></form:label><br />
<form:checkboxes items="${allAuthorities}" path="myAuthorities" itemLabel="name"/><form:errors path="myAuthorities" /></p>
<p><input type="submit"/></p>
</fieldset>
</form:form>
The jsp gets allAuthorities from this:
#ModelAttribute("allAuthorities")
public List<Authority> populateAuthorities() {
return authorityService.findAll();
}
When I fill the form I get:
Failed to convert property value of
type java.lang.String to required type
java.util.Collection for property
myAuthorities; nested exception is
java.lang.IllegalStateException:
Cannot convert value of type
[java.lang.String] to required type
[com.tda.model.applicationuser.Authority]
for property myAuthorities[0]: no
matching editors or conversion
strategy found
Which is the correct way to solve this?
HTML forms work with string values only, when your Authority is a complex bean. You need to configure a PropertyEditor to perform conversion between Authority and String:
#InitBinder
public void initBinder(WebDataBinder b) {
b.registerCustomEditor(Authority.class, new AuthorityEditor());
}
private class AuthorityEditor extends PropertyEditorSupport {
#Override
public void setAsText(String text) throws IllegalArgumentException {
setValue(authorityService.findById(Long.valueOf(text)));
}
#Override
public String getAsText() {
return ((Authority) getValue()).getId();
}
}

Resources