Validation of wrapper in spring form - spring

I've wrapped two objects I wanted to use in one form. Here is the wrapper class:
public class UserCustomer {
User user;
Customer customer;
//Getters and setters
//Constructors
Controller:
#Controller
public class RegisterController {
#RequestMapping("/register")
public String showRegister(Model model){
model.addAttribute("userCustomer", new UserCustomer(new User(), new Customer()));
return "register";
}
#RequestMapping(value="/doRegister", method = RequestMethod.POST)
public String doRegister(Model model, #Valid UserCustomer userCustomer, BindingResult bindingResult){
if(bindingResult.hasErrors()){
return "register";
}
System.out.println(userCustomer.getUser());
return "registered";
}
}
And the form:
<h3 class="new-models">For New Customers</h3>
<div class="register">
<sf:form method="post" action="${pageContext.request.contextPath}/doRegister" commandName="userCustomer">
<div class="register-top-grid">
<h3>PERSONAL INFORMATION</h3>
<div>
<span>First Name<label>*</label></span>
<sf:errors path="customer.name" cssClass="alert-danger"/>
<sf:input name="name" type="text" path="customer.name"/>
</div>
<div>
<span>Last Name<label>*</label></span>
<sf:errors path="customer.surname" cssClass="alert-danger"/>
<sf:input name="surname" type="text" path="customer.surname"/>
</div>
<div>
<span>Email Address<label>*</label></span>
<sf:errors path="user.email" cssClass="alert-danger"/>
<sf:input name="email" type="text" path="user.email"/>
</div>
<div>
<span>Username<label>*</label></span>
<sf:errors path="user.username" cssClass="alert-danger"/>
<sf:input name="username" type="text" path="user.username"/>
</div>
<div class="clearfix"> </div>
<!--a class="news-letter" href="#">
<label class="sf:checkbox">
<!--sf:input type="checkbox" name="newsletter" path="customer.newsletter" checked=""/><i> </i>Sign Up for Newsletter</label>
</a-->
<a class="news-letter" href="#"></a>
</div>
<div class="register-bottom-grid">
<h3>LOGIN INFORMATION</h3>
<div>
<span>Password<label>*</label></span>
<sf:errors path="user.password" cssClass="alert-danger"/>
<sf:input name="password" type="password" path="user.password"/>
</div>
<div>
<span>Confirm Password<label>*</label></span>
<sf:input type="password" path=""/>
</div>
</div>
<div class="clearfix"> </div>
<div class="register-but">
<input type="submit" value="register">
<div class="clearfix"> </div>
</div>
</sf:form>
</div>
Everything seems to be working fine, except for validation. I don't really know how to make validation in this wrapper.
Example validation for email in User class:
#Pattern(regexp = ".*\\#.*\\..*")

I have solved myself this problem, it's way more simple than I have expected it would be.
Validation works simply by adding #Valid annotation above user and customer fields in this wrapper, so that spring knows to actually validate them "deeper".

Related

The server cannot process the request because of Spring MVC <form:select>

I am creating a formular in order to populate some entities. I run into problems when I am trying to POST a form which contains a Spring MVC <form:select> field.
In Eclipse I do not receive any error or warning message, while in the browser I get a http status 400 - Bad Request.
Type Status Report
Description The server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing).
Extra explanations:
I have a StudentDetails entity which contains 2 fields that I am interested in at the moment, Classroom classroom and ParentsDetails parentsDetails.
The Classroom objects are already created and all of them will be stored in a LinkedHashMap<Classroom, String> as a model attribute (i am doing this in the saveAccountDetails method from the Controller).
The ParentsDetails object will be created after the StudentDetails entity will be saved with the selected classroom.
When I submit the form as I mentioned above I run into an error but without any(or relevant) error message.
I spent some time debugging and trying different approaches of handling that map of Classrooms, but none of them worked.
What is actually happening, the controller method saveStudentDetails is not called anymore.
The issue must come from that form:select because if I get rid of this input, the controller method will be called and will let me advance in creating the ParentsDetails entity.
I have no clue what is wrong.
I used previously this form:select but the LinkedHashMap contained just Strings, without any objects and it worked. I think thats my issue.
StudentDetails.java
#Entity
#Table(name="student_details")
public class StudentDetails {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="current_year_of_study")
private Integer currentYearOfStudy;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="parents_details_id")
private ParentsDetails parentsDetails;
#ManyToOne(cascade={CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH})
#JoinColumn(name="class_id")
private Classroom classroom;
#OneToOne(mappedBy="studentDetails", cascade={CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH})
private User user;
#ManyToMany
#JoinTable(name="course_studentdetails",
joinColumns=#JoinColumn(name="student_details_id"),
inverseJoinColumns=#JoinColumn(name="course_id")
)
private List<Course> courses;
... (Constructors, getters setters)
Controller.java
#PostMapping("/save-account-details")
public String saveAccountDetails(#ModelAttribute("theAccountDetails") AccountDetails theAccountDetails, #RequestParam("userUsername") String username, Model theModel) {
User theUser = userService.getUser(username);
theUser.setAccountDetails(theAccountDetails);
accountDetailsService.saveAccountDetails(theAccountDetails);
userService.saveUser(theUser);
theModel.addAttribute("theUser", theUser);
theModel.addAttribute("theStudentDetails", new StudentDetails());
theModel.addAttribute("classroomsList", classroomService.getSchoolClassrooms(theUser.getAccountDetails().getCity()));
theModel.addAttribute("entity", "StudentDetails");
return "create-user";
}
#PostMapping("/save-student-details")
public String saveStudentDetails(#ModelAttribute("theStudentDetails") StudentDetails theStudentDetails, #RequestParam("userUsername") String username, Model theModel) {
User theUser = userService.getUser(username);
theUser.setStudentDetails(theStudentDetails);
studentDetailsService.saveStudentDetails(theStudentDetails);
userService.saveUser(theUser);
theModel.addAttribute("theUser", theUser);
theModel.addAttribute("theParentsDetails", new ParentsDetails());
theModel.addAttribute("entity", "ParentsDetails");
return "create-user";
}
#PostMapping("/save-parents-details")
public String saveParentsDetails(#ModelAttribute("theParentsDetails") ParentsDetails theParentsDetails, #RequestParam("userUsername") String username, Model theModel) {
User theUser = userService.getUser(username);
theUser.getStudentDetails().setParentsDetails(theParentsDetails);
parentsDetailsService.saveParentsDetails(theParentsDetails);
userService.saveUser(theUser);
theModel.addAttribute("theUser", theUser);
theModel.addAttribute("theParentsDetails", new ParentsDetails());
theModel.addAttribute("entity", "ParentsDetails");
return "create-user";
}
create-user.jsp
<c:if test="${entity == 'StudentDetails'}">
<c:url var="saveStudentDetails" value="save-student-details">
<c:param name="userUsername" value="${theUser.username}" />
</c:url>
<form:form action="${saveStudentDetails}" modelAttribute="theStudentDetails" method="POST">
<form:hidden path="id" />
<div class="form-area">
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="inputGroup-sizing-default">Current Year of Study</span>
</div>
<form:input type="text" class="form-control" path="currentYearOfStudy" aria-label="Default" aria-describedby="inputGroup-sizing-default" />
</div>
<div class="input-group mb-3">
<div class="input-group-prepend">
<label class="input-group-text" for="inputGroupSelect01">Classroom</label>
</div>
<form:select path="classroom" class="custom-select" id="inputGroupSelect01">
<form:option value="" label="Select classroom..." />
<form:options items="${classroomsList}" />
</form:select>
</div>
<button type="submit" class="btn btn-outline-secondary btn-block">Submit</button>
</div>
</form:form>
</c:if>
<c:if test="${entity == 'ParentsDetails'}">
<c:url var="saveParentsDetails" value="save-parents-details">
<c:param name="userUsername" value="${theUser.username}" />
</c:url>
<form:form action="${saveParentsDetails}" modelAttribute="theParentsDetails" method="POST">
<form:hidden path="id" />
<div class="form-area">
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="inputGroup-sizing-default">Father First Name</span>
</div>
<form:input type="text" class="form-control" path="fatherFirstName" aria-label="Default" aria-describedby="inputGroup-sizing-default" />
</div>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="inputGroup-sizing-default">Father Last Name</span>
</div>
<form:input type="text" class="form-control" path="fatherLastName" aria-label="Default" aria-describedby="inputGroup-sizing-default" />
</div>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="inputGroup-sizing-default">Father Telephone</span>
</div>
<form:input type="text" class="form-control" path="fatherTelephone" aria-label="Default" aria-describedby="inputGroup-sizing-default" />
</div>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="inputGroup-sizing-default">Mother First Name</span>
</div>
<form:input type="text" class="form-control" path="motherFirstName" aria-label="Default" aria-describedby="inputGroup-sizing-default" />
</div>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="inputGroup-sizing-default">Mother Last Name</span>
</div>
<form:input type="text" class="form-control" path="motherLastName" aria-label="Default" aria-describedby="inputGroup-sizing-default" />
</div>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="inputGroup-sizing-default">Mother Telephone</span>
</div>
<form:input type="text" class="form-control" path="motherTelephone" aria-label="Default" aria-describedby="inputGroup-sizing-default" />
</div>
<button type="submit" class="btn btn-outline-secondary btn-block">Submit</button>
</div>
</form:form>
</c:if>
If any more code snippets are needed, I will add them as soon as possible. Thank you in advance!
This is the network tab. The object looks like it was submitted with the form...
I will add shortly a video with the application.
Edit: demo link: https://youtu.be/neJOLHL9REo
Try adding multipart form-data to enctype in the form tag, it may works.
FROM:
<form:form action="${saveStudentDetails}"
modelAttribute="theStudentDetails" method="POST">
TO:
<form:form action="${saveStudentDetails}" enctype="multipart/form-data"
modelAttribute="theStudentDetails" method="POST">

How tell the view an error has occured from controller? (Spring MVC)

How can I say trigger error/validation messages in the view from the controller in a better way? Currently, I do this by sending boolean attributes. For example, in creating a product, I have two possible errors. Invalid format of UPC of a product, or duplicate upc. I also have a validati
#RequestMapping("/createProduct")
public String createProduct(Model model, #RequestParam(value = "name") String name,
#RequestParam(value = "upc") String upc, #RequestParam(value = "category") String categoryName,
#RequestParam(value = "description") String description, #RequestParam(value = "price") BigDecimal price,
#RequestParam(value = "stock") int stock){
model.addAttribute("activeTab", 3);
if(Validator.invalidUpcFormat(upc)){
model.addAttribute("invalidFormat", true); //trigger for invalid format
return "management";
}
Category category = productService.getCategory(categoryName);
Product product = new Product(upc, category, name, description, price);
InventoryProduct inventoryProduct = new InventoryProduct(product, stock);
try {
managerService.add(inventoryProduct);
model.addAttribute("productCreated", true);
} catch (DuplicateProductException e) {
model.addAttribute("upc", upc);
model.addAttribute("duplicateProduct", true); // trigger for duplicate product
}
return "management";
}
And here is my view:
<div id="menu3"
class="tab-pane fade <c:if test="${activeTab == 3}">in active</c:if>">
<div class="container-fluid" style="padding: 2%;">
<div class="row">
<div class="col-md-12"
style="padding-left: 15%; padding-right: 15%;">
<c:if test="${productCreated}">
<div class="alert alert-success fade in">
<a href="#" class="close" data-dismiss="alert"
aria-label="close">×</a> <strong>Success!</strong>
Product has been created!
</div>
</c:if>
<c:if test="${duplicateProduct}">
<div class="alert alert-warning fade in">
<a href="#" class="close" data-dismiss="alert"
aria-label="close">×</a> <strong>Oh no!</strong>
Product with the UPC ${upc} already exists!
</div>
</c:if>
<c:if test="${invalidFormat}">
<div class="alert alert-warning fade in">
<a href="#" class="close" data-dismiss="alert"
aria-label="close">×</a> <strong>Oops!</strong>
Invalid UPC format!
</div>
</c:if>
<form
action="${pageContext.request.contextPath}/manager/createProduct"
method="post">
<div class="form-group">
<label for="Name">Name </label> <input type="text" name="name"
class="form-control" required />
</div>
<div class="form-group">
<label for="UPC">UPC </label> <input type="number" name="upc"
class="form-control" required />
</div>
<div class="form-group">
<div class="form-group">
<label for="category">Category</label> <select
class="form-control" name="category" required>
<option selected disabled value="">SELECT CATEGORY</option>
<c:forEach items="${categories}" var="item">
<option>${item.getName()}</option>
</c:forEach>
</select>
</div>
</div>
<div class="form-group">
<label for="description">Description</label>
<textarea class="form-control" rows="5" name="description"></textarea>
</div>
<div class="form-group">
<label for="price">Price </label> <input type="number"
name="price" class="form-control" required />
</div>
<div class="form-group">
<label for="stock">Stock </label> <input type="number"
name="stock" class="form-control" required />
</div>
<button type="submit" class="btn btn-primary">Add
product</button>
</form>
</div>
</div>
</div>
</div>
Is there a better of doing this other than sending boolean triggers?
You could use Spring BindingResult. This is typical filled with the result of Binding and Validation results. But you can also add errors by hand.
But first you need to refactor your code, so that you use an single command/form-backing object instead of all the #Param values
public class CreateProductCommand {
private String name;
private String upc;
private String categoryName;
.... //other fields
public CreateProductCommand (){} //parameter less conturctor
Getter+Setter
}
Controller
#RequestMapping("/createProduct")
public ModelAndView createProduct(CreateProductCommand createProductCommand, BindingResult bindingResult) //Binding result must be the parameter direct next to the object that should been validated!!!
{
if (someustomValidationForUcpFail()) {
bindingResult.rejectValue("upc", //the field name of the invalid field
"error.Message.Key",
"Default Error Message");
}
if (bindingResult.hasErrors()) {
ModelMap model = new ModelMap();
model.add("createProductCommand", createProductCommand);
return new ModelAndView("createForm", model)
} else {
Product product = ...
return new ModelAndView("showProduct", "product", product)
}
}
jsp:
You need to use springs form and input tag:
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:springForm="http://www.springframework.org/tags/form"
version="2.0">
....
<springForm:form action="<c:url value="/manager/createProduct">" method="POST" modelAttribute="createProductCommand">
<springForm:input path="name"/> <form:errors path="name" />
<springForm:input path="ucp"/> <form:errors path="ucp" />
....
</springForm:form>
....

While form submitting using ajax..getting Post not supported error ...don't know what is the error?

I am using form submission using AJAX in Spring MVC and Thymeleaf. When I try to submit it it shows
Post method is not supported
I can't figure out the mistake in my code:
<form class="form-horizontal" action="#" th:action="#{/teacher/teacherProfileUpdation}" th:object="${teacherProfileDetailsList}"
id="saveTeacherForm" method="POST" >
<br />
<div class="row">
<div class="col-lg-14 col-md-12">
<br />
<h5 style="margin-left: 15%;">Personal Details</h5>
<hr />
<div class="form-group">
<label class="col-sm-3 control-label">Name</label>
<div class="col-md-3 col-sm-4 col-xs-4">
<input placeholder="Teacher first name" id="txtTeacherFname" th:field="*{firstName}" type="text" class="form-control" />
</div>
<div class="col-md-3 col-sm-4 col-xs-4">
<input placeholder="Teacher middle name" id="txtTeacherMname" th:field="*{middleName}" type="text" class="form-control" />
</div>
<div class="col-md-3 col-sm-4 col-xs-4">
<input placeholder="Teacher last name" id="txtTeacherLname" th:field="*{lastName}" type="text" class="form-control" />
</div>
</div>
</div>
<div class="col-lg-14 col-md-12">
<div class="form-actions">
<input type="hidden" id="hdnStudentByIdInSchoolAdmin" value="0" />
<input type="button" class="btn btn-info pull-right" id="btnUpdateTeacherProfile" value="Save" />
</div>
</div>
</div>
JS:
saveTeacherProfile :function(){
$("#saveTeacherForm").ajaxForm({
success : function(status) {
alert("success");
},
}).submit();
}
Controller:
#RequestMapping(value = "/updateTeacherProfile", method = RequestMethod.POST)
public String updateTeacherProfile( TeacherProfileDetails teacherProfileDetails){
System.out.println("-----------"+teacherProfileDetails.getFirstName()+"-------------");
System.out.println("-----------"+teacherProfileDetails.getLastName()+"-------------");
System.out.println("-----------"+teacherProfileDetails.getMiddleName()+"-------------");
return "success";
}
You are posting the form, but most likely your Spring controller is not configured to accept POST requests. Try this in your server-side Controller class for this page:
#RequestMapping(..., method = { RequestMethod.GET, RequestMethod.POST }, ...)
public void myControllerMethod()
{
#RequestMapping(..., method = { RequestMethod.GET, RequestMethod.POST }, ...)
public String updateTeacherProfile( TeacherProfileDetails teacherProfileDetails){
//ur Logic
}

Cannot submit a form on SpringMVC

I am fairly new to SpringMVC and have a form that can not submit to the back-end. I created the form as following and when I submit it error 404 will be returned. I changed action to /MyProject/contact but did not work.
<form class="form-horizontal" role="form" method="post"
action="/contact">
<div class="form-group">
<div class="col-md-12">
<label class="sr-only" for="exampleInputName2">Name
</label> <input type="text" class="form-control" id="name"
name="name" placeholder="Your name" value="">
</div>
</div>
<div class="form-group">
<div class="col-md-12">
<label class="sr-only" for="exampleInputName2">Email
Address</label> <input type="email" class="form-control" id="email"
name="email" placeholder="Your email" value="">
</div>
</div>
<div class="form-group">
<div class="col-md-12">
<label class="sr-only" for="exampleInputName2">Phone
Number</label> <input type="number" class="form-control" id="phone"
name="phone" placeholder="Phone number" value="">
</div>
</div>
<div class="form-group">
<div class="col-md-12">
<label class="sr-only" for="exampleInputName2">Enquiry</label>
<textarea class="form-control" rows="4" name="message"
placeholder="Please enter your enquiry"></textarea>
</div>
</div>
<div class="form-group">
<div class="col-md-2 " style="float: right;">
<input id="submit" name="submit" type="submit" value="Send"
class="btn btn-primary">
</div>
</div>
<div class="form-group">
<div class="col-sm-10 col-sm-offset-2">
<! Will be used to display an alert to the user>
</div>
</div>
</form>
Controller
#Controller
public class ContactController {
#RequestMapping(value="/contact", method=RequestMethod.POST)
public String processForm(Contact contact, Model model){
System.err.println("Contact Name is:" + contact.getName());
return null;
}
}
Error
HTTP Status 404 - /contact
type Status report
message /contact
description The requested resource is not available.
Its beacuse spring does not know how to pass the param Contact contact to your controller method. You need to do couple of things to make it work. Change your form to like below.
<form class="form-horizontal" role="form" method="post" modelAttribute="contact" action="/contact">
Your controller to take contact as model attribute.
#Controller
public class ContactController {
#RequestMapping(value="/contact", method=RequestMethod.POST)
public String processForm(#ModelAttribute Contact contact, Model model){
System.err.println("Contact Name is:" + contact.getName());
return null;
}
}
For a better understanding of what a model attribute does, there are plenty of samples and explanation online. Hope this helps.
I could solve the problem by help of minion's answer, following this tutorial and adding following link
#RequestMapping(value = "/contact", method = RequestMethod.GET)
public ModelAndView contactForm() {
System.err.println("here in GET");
return new ModelAndView("contact", "command", new Contact());
}

How to use jsp tag for select?

I have a view model and one of the fields has to be mapped to a dropdown. I manage to populate the dropdown but i don't know how to specify the selected value.
edituser.jsp
<%# include file="/WEB-INF/template/taglibs.jsp"%>
<div class="container">
<%# include file="menu.jsp"%>
<form:form commandName="editForm" method="post" class="form-horizontal form-width">
<fieldset>
<legend>Edit user</legend>
<div class="form-group">
<input class="hidden" type="text" value="${id}" hidden="true" name="id"/>
<label for="firstName" class="col-lg-2 control-label">First
name</label>
<div class="col-lg-10">
<input type="text" class="form-control" id="firstName"
placeholder="First name" name="firstName" value="${fname}">
</div>
</div>
<div class="form-group">
<label for="lastName" class="col-lg-2 control-label">Last
name</label>
<div class="col-lg-10">
<input type="text" class="form-control" id="lastName"
placeholder="Last name" name="lastName" value="${lname}">
</div>
</div>
<div class="form-group">
<label for="select" class="col-lg-2 control-label">Role</label>
<div class="col-lg-10">
<select name="role" class="form-control">
<option value="NONE">Select role</option>
<c:forEach items="${roles}" var="role">
<option value="${role}">${role}</option>
</c:forEach>
</select>
</div>
</div>
<div class="form-group">
<label for="username" class="col-lg-2 control-label">Username</label>
<div class="col-lg-10">
<input type="text" class="form-control" id="username"
placeholder="Username" name="username" value="${username}">
</div>
</div>
<div class="form-group">
<label for="inputPassword" class="col-lg-2 control-label">Password</label>
<div class="col-lg-10">
<input type="password" class="form-control" id="inputPassword"
placeholder="Password" name="password" value="${guid}">
</div>
</div>
<div class="form-group">
<label for="inputPassword" class="col-lg-2 control-label">Confirm password</label>
<div class="col-lg-10">
<input type="password" class="form-control" id="inputPassword"
placeholder="Password" name="confirmedpassword" value="${guid}">
</div>
</div>
<div class="form-group">
<div class="col-lg-10 col-lg-offset-2">
<a class="btn btn-warning" href="http://localhost:8080/Catering/index/users">Cancel</a>
<button type="submit" class="btn btn-warning">Submit</button>
</div>
</div>
</fieldset>
</form:form>
</div>
UsersController
#Controller
public class UsersController {
#RequestMapping(value = "/editUser", method = RequestMethod.GET)
public String getEditUserPage(#RequestParam int id, Model model, Authentication authentication) {
logger.debug("Received request to show edit user page(GET)");
model.addAttribute("username", "You are logged in as " + authentication.getPrincipal());
UserModel user = UserDataAccess.getUserById(id);
model.addAttribute("id", user.getId());
model.addAttribute("fname", user.getFirstName());
model.addAttribute("lname", user.getLastName());
model.addAttribute("role", user.getRole());
model.addAttribute("username", user.getUsername());
model.addAttribute("guid", guid);
model.addAttribute("editForm", new UserViewModel());
return "edituser";
}
}
UserViewModel
public class UserViewModel {
private int id;
private String firstName;
private String lastName;
private String role;
private String username;
private String password;
private String confirmedpassword;
//getters and setters
}
You can do something like (not tested)
<select name="role" class="form-control">
<option value="NONE">Select role</option>
<c:forEach items="${roles}" var="curRole">
<c:choose>
<c:when test="${curRole eq role}">
<option selected="selected">${role}</option>
</c:when>
<c:otherwise>
<option>${role}</option>
</c:otherwise>
</c:choose>
</c:forEach>
</select>
You also must add a Collection in your controller, representing the list of all roles. I cannot see it from your code.
I suggest you to take a look at spring-form.tld facilities, in your particular case: select tags: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/spring-form.tld.html#spring-form.tld.select
Here you can find a simple example on how using spring:form tag: http://www.mkyong.com/spring-mvc/spring-mvc-dropdown-box-example/

Resources