Spring + Thymeleaf type Converter in a form - spring

I'm trying to use a type converter in a Spring boot app and using Thymeleaf but I can't get it working. I've put some code on Github so you can see exactly what I'm trying to do. This is Spring 1.5.1 and Thymeleaf 3.0.3. https://github.com/matthewsommer/spring-thymeleaf-simple-converter
Basically this code is just trying to add a person to a comment object. The person object is null when it gets posted and I don't understand why.
Something that's odd is that the ID of the person isn't being added to the value attribute but it is if th:field="*{body}" is removed. I think it has to do with this: https://github.com/thymeleaf/thymeleaf/issues/495 but I'm currently trying to add BindingResult and it's not working...
My HTML is:
<body>
<div th:if="${personObject != null}" th:text="${personObject.name}"></div>
<form th:action="#{/}" th:object="${comment}" method="post">
<input type="hidden" th:if="${personObject != null}" th:value="${personObject.id}" th:field="*{person}" />
<textarea id="comment" placeholder="Comment..." th:field="*{body}"></textarea>
<button id="comment_submit" type="submit">Comment</button>
</form>
<div th:text="${comment.body}"></div>
</body>
My controller:
#Controller
public class HomeWebController {
#RequestMapping(value = "/", method = RequestMethod.GET)
public String getHome(final HttpServletRequest request, final Map<String, Object> model, #ModelAttribute(value = "comment") Comment comment) {
model.put("personObject", new Person(1, "John Smith"));
return "Home";
}
#RequestMapping(value = "/", method = RequestMethod.POST)
public String postHome(final HttpServletRequest request, final Map<String, Object> model, #ModelAttribute(value = "comment") Comment comment) {
model.put("commentBody", comment.getBody());
model.put("person", comment.getPerson());
return "Home";
}
}
And the converter:
#Component
public class StringToPersonConverter implements Converter<String, Person> {
#Autowired
public StringToPersonConverter() { }
#Override
public Person convert(String id) {
if(id == "1") {
Person person = new Person(1, "John Smith");
return person;
}
return null;
}
}

Hi finally I had to do some changes to make it work, but this is the result class by class.
ConvertorApplication:
#SpringBootApplication
#Configuration
#EnableWebMvc
public class ConvertorApplication extends WebMvcConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(ConvertorApplication.class, args);
}
//Add converter and configuration annotation
#Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToPersonConverter());
}
}
StringToPersonConverter:
#Override
public Person convert(String id) {
//Never compare String with == use equals, the "==" compares memory space not the values
if(id.equals("1")) {
Person person = new Person(1, "John Smith");
return person;
}
return null;
}
HomeWebController
#Controller
public class HomeWebController {
#RequestMapping(value = "/", method = RequestMethod.GET)
public String getHome(final Map<String, Object> model, #ModelAttribute(value = "comment") Comment comment) {
//Initialize the comment with the person inside, no need of personObject object
model.put("comment", new Comment(new Person(1, "John Smith")));
return "Home";
}
#RequestMapping(value = "/", method = RequestMethod.POST)
public String postHome(final Map<String, Object> model,
#ModelAttribute(value = "comment") Comment comment,
#RequestParam(value = "person.id") Person person) {
//from the view retrieve the value person.id which will be used by the converter to build the Person entity
comment.setPerson(person);
model.put("comment", comment);
return "Home";
}
}
Comment (Add empty constructor)
public Comment(){}
Person (Add empty constructor)
public Person(){}
Home.jsp (Basically remove personObject, not need)
<!DOCTYPE html>
<html xmlns:th="//www.thymeleaf.org">
<body>
<div th:text="${comment.person.name}"></div>
<form th:action="#{/}" th:object="${comment}" method="post">
<input type="hidden" th:field="*{person.id}" />
<textarea id="comment" placeholder="Comment..." th:field="*{body}"></textarea>
<button id="comment_submit" type="submit">Comment</button>
</form>
<div th:text="${comment.body}"></div>
</body>
</html>
That's would be everything to make it work.

Related

NullPointException while uploading file in spring

I try to upload a .csv file's data into database, but when i upload and submit it, it throws nullpointexception. Means, when I print name in controller, name is printed, but when i try to get the file, it show null.
FileUpload model class
public class FileUpload {
private CommonsMultipartFile[] files;
private String name;
// Getters and setters
}
Controller
#RequestMapping(value = "uploadPage", method = RequestMethod.GET)
public ModelAndView uploadPage() {
ModelAndView model = new ModelAndView("upload_page");
FileUpload formUpload = new FileUpload();
model.addObject("formUpload", formUpload);
return model;
}
#RequestMapping(value = "/doUpload", method = RequestMethod.POST)
public String doUpload(#ModelAttribute("formUpload") FileUpload fileUpload, BindingResult result) throws IOException, JAXBException {
System.out.println("myfirl "+fileUpload.getFiles()); // output is null
System.out.println("name "+fileUpload.getName()); // name is displaying
//other stuffs
}
upload_page
<spring:url value="/doUpload" var="doUploadURL"/>
<form:form method="post" modelAttribute="formUpload" action="${doUploadURL }" enctype="multipart/form-data">
<form:input path="files" type="file" multiple="multiple"/>
<form:input path="name" type="text"/>
<button type="submit">Upload</button>
</form:form>
WebConfig
#Bean(name="multipartResolver")
public CommonsMultipartResolver getResolver(){
CommonsMultipartResolver commonsMultipartResolver=new CommonsMultipartResolver();
commonsMultipartResolver.setMaxUploadSizePerFile(20*1024*1024);
return commonsMultipartResolver;
}
I tried to sort it out in many ways, but failed. Anyone try to sort it out? Thanks in advance
#RequestMapping(value = "/uploadFile", method = RequestMethod.POST)
#ResponseBody
public String uploadFileHandler(#RequestParam("file") MultipartFile file) {
if (!file.isEmpty()) {
System.out.println(file.getName);
}
}
or you can do that
#RequestMapping(value = "/uploadFile", method = RequestMethod.POST)
public String uploadFileHandler(MultipartHttpServletRequest request) {
Iterator<String> itr = request.getFileNames();
while (itr.hasNext()){
System.out.println(itr.next().toString());
}
....
}

Spring - Model attributes after BindingResult with errors

In the following piece of code I just want to create a new user and link it to the selected groups.
Everything works fine when the user and group are valid. The problem comes when the bindingresult has errors. The controller detects such error (all fine so far) and returns the same view (I want to keep the data entered by the user) but the list of groups is empty (I have discovered that, after showing again the view, userform.groups is null).
Has anyone a clue about what the problem could be?
UserForm
#Component
public class UserForm {
#Valid
private User user;
#Valid
private Collection<Group> allGroups;
// Setters and getters
}
UserController
#Controller
public class UserController {
#Autowired
UserGroupService userGroupService;
#Autowired
BCryptPasswordEncoder passwordEncoder;
#InitBinder
public void initBinder (WebDataBinder binder) {
binder.registerCustomEditor(Set.class, "userform.user.groups", new GroupListEditor(userGroupService));
}
#RequestMapping(value = "/admin/users/CreateUser", method = RequestMethod.GET)
public ModelAndView createUsetGet () {
ModelAndView mav = new ModelAndView("/admin/users/CreateUser");
UserForm userForm = new UserForm();
userForm.setUser(new User());
userForm.setGroups(userGroupService.getAllEnabledGroups());
mav.addObject("userform", userForm);
return mav;
}
#RequestMapping(value = "/admin/users/CreateUser", method = RequestMethod.POST)
public String createUserPost (#Valid #ModelAttribute("userform") UserForm userForm, BindingResult result) {
if (result.hasErrors() == true) {
return "/admin/users/CreateUser";
}
userForm.getUser().setPassword(passwordEncoder.encode(userForm.getUser().getPassword()));
userGroupService.saveUser(userForm.getUser());
return "redirect:/admin/users/ViewUsers";
}
}
CreateUser.jsp (Only piece regarding the groups)
<form:form modelAttribute="userform" method="post">
Username:
<form:input path="user.loginName"/>
<!-- More fields -->
<form:select path="user.groups" multiple="true">
<form:options items="${userform.groups}" itemValue="id" itemLabel="name" />
</form:select>
<button type="submit">Create</button>
</form:form>
Any help is appreciated!
The object gets recreated and values are bound to the resulting object. Which means no group objects.
Also those shouldn't be in the object at all. To solve use a #ModelAttribute annotated method, which will be invoked for each request handling method and create an object and fill the list of groups.
#ModelAttribute
public void init(Model model) {
UserForm userForm = new UserForm();
userForm.setUser(new User());
model.addAttribute("userform", userForm);
model.addAtrribute("groups", userGroupService.getAllEnabledGroups());
}
#RequestMapping(value = "/admin/users/CreateUser", method = RequestMethod.GET)
public String createUsetGet () {
return "/admin/users/CreateUser";
}
#RequestMapping(value = "/admin/users/CreateUser", method = RequestMethod.POST)
public String createUserPost (#Valid #ModelAttribute("userform") UserForm userForm, BindingResult result) {
if (result.hasErrors() == true) {
return "/admin/users/CreateUser";
}
userForm.getUser().setPassword(passwordEncoder.encode(userForm.getUser().getPassword()));
userGroupService.saveUser(userForm.getUser());
return "redirect:/admin/users/ViewUsers";
}
Ofcourse your jsp has to change slightly also.
<form:select path="user.groups" multiple="true">
<form:options items="${groups}" itemValue="id" itemLabel="name" />
</form:select>
There is one drawback of using this approach now the userGroupService.getAllEnabledGroups() is called for each incoming request. This might not be needed. You could store those in the session using the #SessionAttributes annotation on the class.
#Controller
#SessionAttributes("groups")
public class UserController {
#Autowired
UserGroupService userGroupService;
#Autowired
BCryptPasswordEncoder passwordEncoder;
#InitBinder
public void initBinder (WebDataBinder binder) {
binder.registerCustomEditor(Set.class, "userform.user.groups", new GroupListEditor(userGroupService));
}
#ModelAttribute("groups")
public List<Group> groups() {
return userGroupService.getAllEnabledGroups();
}
#ModelAttribute("userform")
public UserForm userform() {
UserForm userForm = new UserForm();
userForm.setUser(new User());
return userForm;
}
#RequestMapping(value = "/admin/users/CreateUser", method = RequestMethod.GET)
public String createUsetGet () {
return "/admin/users/CreateUser";
}
#RequestMapping(value = "/admin/users/CreateUser", method = RequestMethod.POST)
public String createUserPost (#Valid #ModelAttribute("userform") UserForm userForm, BindingResult result, SessionStatus status) {
if (result.hasErrors() == true) {
return "/admin/users/CreateUser";
}
userForm.getUser().setPassword(passwordEncoder.encode(userForm.getUser().getPassword()));
userGroupService.saveUser(userForm.getUser());
status.setComplete();
return "redirect:/admin/users/ViewUsers";
}
}
You will then need, on success, to tell the SessionStatus that you are finished. If you don't do this your session might pollute.
It's because the information about the validation errors is lost after redirect.
You can solve this using RedirectAttributes. Check this tutorial.

How to use ValidationUtils in spring

I want to use ValidationUtils as follows. But I cannot instantiate errors object since Errors is an Interface. Can you tell me how I can make this working without using a validator?
if(visitorDetails==null)
{
Errors errors;
visitorDetails=new Visitor();
ValidationUtils.rejectIfEmpty(errors, "VisitorInfo", "Selected Visitor Details Not Found");
}
Read this : Validation...
However you must implement the Validation interface in a class, and than use it to validate your object, and to do that you autowire validator in your controller..
This is an example:
public class PersonValidator implements Validator {
/**
* This Validator validates *just* Person instances
*/
public boolean supports(Class clazz) {
return Person.class.equals(clazz);
}
public void validate(Object obj, Errors e) {
ValidationUtils.rejectIfEmpty(e, "name", "name.empty");
Person p = (Person) obj;
if (p.getAge() < 0) {
e.rejectValue("age", "negativevalue");
} else if (p.getAge() > 110) {
e.rejectValue("age", "too.darn.old");
}
}
}
and in your controller:
....
#Autowired
private PersonValidator personValidator;
#InitBinder
protected void initBinder(final HttpServletRequest request, final ServletRequestDataBinder binder) {
binder.addValidators(personValidator);
}
...
Assuming you are using Spring Boot.
If using application.properties (under project/src/resources) put the following in it:
spring.messages.basename=validation
Now put a validation.properties (under project/src/resources) and put the following (for example) in it:
NotEmpty=This field is required.
Your model (AppUser in this case) should have:
private String useremail;
getters/setters;
Create a component (Class) like this (example):
#Component
public class UserAddValidator implements Validator {
#Autowired
private UserService userService;
#Override
public boolean supports(Class<?> aClass) {
return AppUser.class.equals(aClass);
}
#Override
public void validate(Object o, Errors errors) {
AppUser user = (AppUser) o;
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "useremail", "NotEmpty");
}
}
The following goes to your controller:
#RequestMapping(value = "registration", method = RequestMethod.POST)
public String registration(#ModelAttribute("userForm") AppUser userForm, BindingResult bindingResult, Model model) {
useraddValidator.validate(userForm, bindingResult);
if (bindingResult.hasErrors()) {
return "userregistration";
}
userService.save(userForm);
model.addAttribute("success", "User " + userForm.getUsername() + " created successfully");
return "success";
}
Last but not the least, in your view put similar to this:
<div class="row">
<label class="col-md-3" for="useremail">Email-ID</label>
<div class="col-md-8">
<spring:bind path="useremail">
<div class="form-group ${status.error ? 'has-error' : ''}">
<form:input type="text" path="useremail" class="form-control"
placeholder="Email-id" autofocus="true"></form:input>
<form:errors path="useremail">${emailerror}</form:errors>
</div>
</spring:bind>
</div>
</div>
The result should look (something) like below:
You can look HERE for more validations.
If I understand your question correctly you want to get the errors object.
In your case I would suggest below approach.
if(visitorDetails==null)
{
visitorDetails=new Visitor();
Errors errors = new BeanPropertyBindingResult(visitorDetails, visitorDetails.getClass().getName());
ValidationUtils.rejectIfEmpty(errors, "VisitorInfo", "Selected Visitor Details Not Found");
}
Let me know if you need more help.
you can use it to make some constraint on some of your field like show error when the field is empty or emptywithspace , this class already contain some static method that can do that
below an exemple for using ValidationUtils class
public class UserValidator implements Validator {
public boolean supports(Class clazz) {
// TODO Auto-generated method stub
return Employee.class.equals(clazz);
}
public void validate(Object target, Errors errors) {
// TODO Auto-generated method stub
ValidationUtils.rejectIfEmpty(errors, "email", "email");
ValidationUtils.rejectIfEmpty(errors, "password", "password");
Employee emplo = (Employee) target;
if(emplo.getEmail() != null && emplo.getEmail()=="aa") {
errors.rejectValue("email", "email invalide ");
}

Neither BindingResult nor plain target object for bean name 'loginuser' available as request attribute

#Controller
#RequestMapping("Page/Login.do")
public class HomeController
{
#RequestMapping(method = RequestMethod.GET)
protected String showLoginPage(HttpServletRequest req,BindingResult result) throws Exception
{
loginuser lu=new loginuser();
lu.setLoginn("Amit");
System.out.println(lu.getLoginn());
return "Login";
}
}
Above code is ##HomeController.java##
loginuser.java
package Com.Site.Name.Order;
public class loginuser
{
private String Loginn;
public String getLoginn()
{
System.out.println("hi i m in login get");
return Loginn;
}
public void setLoginn(String loginn)
{
System.out.println("I m in Loin set");
Loginn = loginn;
}
}
My JSP PAGE IS
Login.jsp
<form:form action="Login.do" method="post" commandName="loginuser">
<div id="Getin">
<img alt="" src="Image/loginttt.png">
</div>
<div id="login">
</div>
<form:input path="Loginn"/>
<input type="submit" value="LOGIN"/>
</form:form>
You are trying to use a Model attribute (commandName) but there is no such attribute added to the model or request attributes. You need to add it. Also, the BindingResult in your handler makes no sense. Remove it.
#RequestMapping(method = RequestMethod.GET)
protected String showLoginPage(HttpServletRequest req, Model model) throws Exception
{
loginuser lu=new loginuser();
lu.setLoginn("Amit");
System.out.println(lu.getLoginn());
model.addAttribute("loginuser", lu);
return "Login";
}
The Model attributes are added to the request attributes and are therefore available in the jsp.

List<Foo> as form backing object using Spring 3 MVC, correct syntax?

I want to do something like this, where Foo is a class with one String field name, and getter/setter:
<form:form id="frmFoo" modelAttribute="foos">
<c:forEach items="${foos}" var="foo">
<form:input path="${foo.name}" type="text"/>
And then submit the complete list of Foos with updated names?
My controller looks like this:
#RequestMapping(value = "/FOO", method = RequestMethod.POST)
public String getSendEmail(List<Foo> foos, Model model) {
// ...
}
Maybe this answersyour question:
CONTROLLER :
#Controller("/")
public class FooController{
//returns the ModelAttribute fooListWrapper with the view fooForm
#RequestMapping(value = "/FOO", method = RequestMethod.GET)
public String getFooForm(Model model) {
FooListWrapper fooListWrapper = new FooListWrapper();
fooListWrapper.add(new Foo());
fooListWrapper.add(new Foo());
//add as many FOO you need
model.addAttribute("fooListWrapper", fooListWrapper);
return "fooForm";
}
#RequestMapping(value = "/FOO", method = RequestMethod.POST)
public String postFooList(#ModelAttribute("fooListWrapper")FooListWrapper fooListWrapper, Model model) {
//...........
}
}
FOO LIST WRAPPER :
public class FooListWrapper {
private List<Foo> fooList;
public FooListWrapper() {
this.fooList = new ArrayList<Foo>();
}
public List<Foo> getFooList() {
return fooList;
}
public void setFooList(List<Foo> fooList) {
this.fooList = fooList;
}
public void add(Foo foo) {
this.fooList.add(foo);
}
}
FOO CLASS :
public class Foo {
private String name;
public Foo() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
JSP VIEW (name = fooForm):
<c:url var="fooUrl" value="/FOO"/>
<form:form id="frmFoo" action="${fooUrl}" method="POST" modelAttribute="fooListWrapper">
<c:forEach items="${fooListWrapper.fooList}" varStatus="i">
<form:input path="fooList[${i.index}].name" type="text"/>
</c:forEach>
<button>submit</button>
</form:form>
Although the above answer works, here's an alternate that does not require you to create a wrapper class/ form class.
Model And Controller
public class Foo {
private String name;
private List<Foo> fooList; //**must create this list, also getter and setter**
public Foo() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List getFooList() {
return fooList;
}
public void setFooList(String fooList) {
this.fooList = fooList;
}
}
#Controller("/")
public class FooController{
//returns the ModelAttribute fooListWrapper with the view fooForm
#RequestMapping(value = "/FOO", method = RequestMethod.GET)
public String getFooList(Model model) {
List<Foo> fooList = service.getFooList();
model.addAttribute("fooList", fooList);
return "list_foo"; //name of the view
}
#RequestMapping(value = "/FOO", method = RequestMethod.POST)
public String postFooList(#ModelAttribute("foo")Foo foo, Model model) {
List<Foo> list = foo.getFooList(); // **This is your desired object.
//If you debug this code, you can easily find this is the list of
//all the foo objects that you wanted, provided you pass them properly.
//Check the jsp file to see one of the ways of passing such a list of objects**
//Rest of the code
}
}
JSP View
<form:form id="form" action="<paste-target-url-here>" method="POST" modelAttribute="fooList">
<c:forEach items="${fooList}" varStatus="i">
<form:input path="fooList[${i.index}].name" type="text"/>
<!-- Here you are setting the data in the appropriate index which will be caught in the controller -->
</c:forEach>
<button>submit</button>
</form:form>

Resources