hibernate validator doesn't show error message - java

Hibernate Validator doesn't show error message. What did i miss? Please see my code below.
Here is a dependency:
<!-- Hibernate Validator -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.2.0.Final</version>
</dependency>
And Entity with annotated column:
#Entity
#Table(name = "transport")
public class Transport {
....
#NotEmpty
#Column(name = "name")
private String name;
....
}
Here are methods from controller:
//show all and add form
#RequestMapping (value = "/admin/transports", method = RequestMethod.GET)
public String findAll(ModelMap map){
List<Transport> transports = transportService.findAll();
map.put("transport", new Transport());
map.put("transports", transports);
return "admin/transports/list";
}
//add new
#RequestMapping(value = "/admin/transport/add", method = RequestMethod.POST)
public String addTypeShop(#ModelAttribute("type") #Valid Transport transport, BindingResult result) {
if (result.hasErrors()) {
return "redirect:/admin/transports";
} else {
this.transportService.addTransport(transport);
return "redirect:/admin/transports";
}
}
And jsp page:
<form:form role="form" action="/admin/transport/add" method="post" commandName="transport">
<div class="row">
<div class="col-lg-6">
<div class="form-group">
<label for="name">Name</label>
<form:input type="text" path="name" class="form-control input-sm" id="name" autofocus="true"/>
<form:errors path="name"/>
</div>
</div>
<input type="submit" class="btn btn-sm btn-primary" value="Add" onclick="loading()"/>
</form:form>

You do not have any code showing the errors back to the end user.
Please see the following link for an example:
http://www.mkyong.com/spring-mvc/spring-mvc-form-errors-tag-example/
Also, It doesn't look like you actually set your command object in your form. You might not have shown that code though.
Small side note, I would not do a redirect if you have validation errors, just send them directly back to the page they came from.
EDIT
After further review, I do see that you have . Normally this goes outside and above the form itself.
<form:errors path="transport"/>
<form:form role="form" action="/admin/transport/add" method="post" commandName="transport">
<div class="row">
<div class="col-lg-6">
<div class="form-group">
<label for="name">Name</label>
<form:input type="text" path="name" class="form-control input-sm" id="name" autofocus="true"/>
</div>
</div>
<input type="submit" class="btn btn-sm btn-primary" value="Add" onclick="loading()"/>
</div>
</form:form>

Related

Cannot get value from simple form user input in Spring boot application?

I'm trying to implement a login form in a Spring boot application. It has an email and a password field. The email field failed to get user input, here is the form:
<form th:action="#{/login}" method="get" th:object="${loginForm}" style="max-width: 600px; margin: 0 auto;">
<div class="m-3">
<div class="form-group row">
<label class="col-4 col-form-label">E-mail: </label>
<div class="col-8">
<input type="text" th:field="*{email}" name="q" class="form-control" required />
</div>
</div>
<div class="form-group row">
<label class="col-4 col-form-label">Password: </label>
<div class="col-8">
<input type="password" th:field="*{password}" class="form-control" required/>
</div>
</div>
<div>
<button type="submit" class="btn btn-primary">Log in</button>
</div>
</div>
</form>
Here is the controller:
#GetMapping("login")
public ModelAndView login(Model model, #RequestParam(name = "q", required = false) Optional<String> email) {
Optional<UserDto> aUser;
System.out.println(email);
if (email.isPresent()) {
aUser = userService.getAUserByEmail(email.get());
model.addAttribute("user", aUser);
var mv = new ModelAndView("user/user-list", model.asMap());
return mv;
} else {
model.addAttribute("loginForm", new LoginForm());
return new ModelAndView("/login/login-form", model.asMap());
}
}
I thought the #RequestParam(name = "q") and name="q" in html would do the job, but I always get Optional.empty for email. Any idea what's wrong here?
UPDATE:
From the answers I changed controller to this:
#GetMapping("login")
public ModelAndView login(Model model, LoginForm loginForm) {
Optional<UserDto> aUser;
if (loginForm.getEmail() != null) {
aUser = userService.getAUserByEmail(loginForm.getEmail());
model.addAttribute("user", aUser);
var mv = new ModelAndView("user/user-list", model.asMap());
return mv;
} else {
model.addAttribute("loginForm", new LoginForm());
return new ModelAndView("/login/login-form", model.asMap());
}
}
login-form.html to this:
<form th:action="#{/login}" method="get" th:object="${loginForm}" style="max-width: 600px; margin: 0 auto;">
<div class="m-3">
<div class="form-group row">
<label class="col-4 col-form-label">E-mail: </label>
<div class="col-8">
<input type="text" th:field="*{email}" class="form-control" required />
</div>
</div>
<div class="form-group row">
<label class="col-4 col-form-label">Password: </label>
<div class="col-8">
<input type="password" th:field="*{password}" class="form-control" required/>
</div>
</div>
<div>
<button type="submit" class="btn btn-primary">Log in</button>
</div>
</div>
</form>
I also have LoginForm.java like this
#Data
#AllArgsConstructor
#NoArgsConstructor
public class LoginForm {
private String email;
private String password;
}
but still not getting user email field input?
The way you have set up your form, it's mapping the value of your email input field to the property email (that's what th:field="*{email}" means) of an object called loginForm (that's what th:object="${loginForm}" means). Neither of these seem to be used or even exist in your login() method. You need to either change what you use in your controller to match what you have in your Thymeleaf template, or change your Thymeleaf template to actually reference what you are using in your controller.
The problem in your code is located under th:object="${loginForm}"
With this you inform spring to bind the data sent from the form into an object named loginForm.
So Spring actually expects the controller to be
#GetMapping("login")
public ModelAndView login(Model model, LoginForm loginForm) {
....
and inside LoginForm a field named email will contain the value sent from the form, as you have declared with <input type="text" th:field="*{email}" .... >
If you don't want the data to be bound into an object from Spring Mvc then
remove the th:object="${loginForm}"
use the
<input type="text" th:name="q" class="form-control" required />
and then the controller will receive the sent value as a query parameter
#GetMapping("login")
public ModelAndView login(Model model, #RequestParam(name =
"q", required = false) Optional<String> email) {

java spring - post request with additional value

I have implemented a registration process where you can send user data to the controller via post request.
The post request works fine, however now I want to pass another value (role, Long) from the form to the controller that is not an attribute of the user model.
That part is not working.
Does anyone know why?
HTML:
<form action="add_user" method="post" class="form-horizontal" th:object="${user}">
<div class="form-group">
<div class="col-sm-offset-1 col-sm-10">
<input th:field="*{username}" class="form-control" placeholder="Person ID" type="text" name="id" id="id"/>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-1 col-sm-10">
<input th:field="*{firstName}" class="form-control" placeholder="First Name" type="text" name="firstname" id="firstname"/>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-1 col-sm-10">
<input th:field="*{lastName}" class="form-control" placeholder="Last Name" type="text" name="lastname" id="lastname"/>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-1 col-sm-10">
<input th:field="*{password}" class="form-control" placeholder="Password" type="password" name="password" id="password"/>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-1 col-sm-10">
<select th:field="${role}" class="form-control" id="role">
<option value="1">Admin</option>
<option value="2" >User</option>
</select>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-1 col-sm-10">
<button type="submit" class="btn btn-success" value="Submit">Save</button>
</div>
</div>
</form>
Controller:
#RequestMapping(value = "/users", method = RequestMethod.GET)
public String showUsers(Model model)
model.addAttribute("user", new User());
model.addAttribute("role", new Long(2));
return "users";
}
And:
#RequestMapping(value = "/add_user", method = RequestMethod.POST)
public String handleNewUser(#ModelAttribute("user") User user, BindingResult bindingResult, Model model, long role) {
if (user != null) {
System.out.println(role);
userService.save(user);
}
return "redirect:/users";
}
th:field="${role}" means name of field in the model object, not its value. You probably want to write th:value="${role}" instead of this.

How to hold filter parameters. Pagination

I have some problem with search filter and pagination. Filter works good, but i can't say the same about pagination.
I have a table with users. I can show all users or by filter(by name, by email). When i'm using filter and trying to go to second page, it return all users without filtration. I understand why it happens, because has no filter parameters. Help me find solution. How can i hold selected filters? Something with sessions?
Here my code.
My Controller
#Controller
#RequestMapping(value = "/admin")
public class AdminController {
#Autowired
UserService userService;
#RequestMapping(value = "/edit-user", method = RequestMethod.GET)
public ModelAndView editUsers(#RequestParam(value = "page", defaultValue = "0") Integer page,
#RequestParam(value = "pattern", required = false) String pattern,
#RequestParam(value = "category", required = false) String category) {
ModelAndView view = new ModelAndView("edit-user");
if(pattern == null){
Page<User> userList = userService.findAll(page);
view.addObject("userList", userList.getContent()).addObject("maxPage", userList.getTotalPages());
}else if(category.equals("username")){
Page<User> userList = userService.findByUsernameContaining(pattern, page);
view.addObject("userList", userList.getContent()).addObject("maxPage", userList.getTotalPages());
}else {
Page<User> userList = userService.findByEmailContaining(pattern, page);
view.addObject("userList", userList.getContent()).addObject("maxPage", userList.getTotalPages());
}
return view;
}
}
My part of JSP
<div class="row top-buffer">
<div class="col-md-4 col-md-offset-4">
<form class="form-inline text-center" role="form" method="get" action="/admin/edit-user?pattern=pattern?category=category">
<fieldset>
<!-- Search Name -->
<div class="form-group">
<label class="sr-only" for="item-name">Product Name</label>
<input id="item-name" name="pattern" placeholder="..." class="form-control">
</div>
<!-- Search Category -->
<div class="form-group">
<label class="sr-only" for="item-category">Product Category</label>
<select id="item-category" name="category" class="form-control">
<option value="username" selected>By username</option>
<option value="email">By email</option>
</select>
</div>
<!-- Search Action -->
<div class="form-group">
<button type="submit" class="btn btn-primary"><span
class="glyphicon glyphicon-search" aria-hidden="true"></span></button>
</div>
</fieldset>
</form>
</div>
</div>
<div class="row top-buffer">
<div class="col-md-8 col-md-offset-2">
<table class="table">
<thead>
<th>Id</th>
<th>Username</th>
<th>Email</th>
<th>Password</th>
</thead>
<c:forEach var="user" items="${userList}">
<tr>
<td>${user.id}</td>
<td>${user.username}</td>
<td>${user.email}</td>
<td>${user.password}</td>
<td>
<button type="button" class="btn btn-info btn-sm editButton" data-toggle="modal"
data-target="#myModal" data-id="${user.id}">Edit
</button>
</td>
</tr>
</c:forEach>
</table>
</div>
</div>
<div class="row">
<div class="col-md-4 pull-right">
<ul class="pagination">
<c:forEach begin="0" end="${maxPage - 1}" var="i">
<li>${i+1}</li>
</c:forEach>
</ul>
</div>
</div>
In your case, putting filter information in session should be OK. You can create a Filter enum that has e.g. name, email fields. Store/update a Filter instance in session every time user wants to filter something.
You can attach your filters to your link(page number in your case) as a query string like so:
${i+1}
In your controller, you need to get the filter information and handle it accordingly (get the value of param filterby, which is name or email). If the filterby param has no value, then no filter is chosen.

Spring MVC Not Displaying Form Errors

Here's my controller
#RequestMapping(value = "/save",method = RequestMethod.POST)
public ModelAndView saveUserAccount(#ModelAttribute("account") UserAccountForm userAccount, BindingResult result){
AddUserValidator userValidator = new AddUserValidator();
userValidator.validate(userAccount,result);
boolean hasErrors = result.hasErrors();
if(hasErrors){
return render(ADD_USER_VIEW)
.addAttr("ACCOUNT_ROLES", Arrays.asList(AccountRole.values()))
.addAttr("TENANTS",tenantInfoService.getAll())
.addAttr("errors",result)
.addAttr("account",userAccount).toMav();
}
return render(ADD_USER_VIEW)
.addAttr("ACCOUNT_ROLES", Arrays.asList(AccountRole.values()))
.addAttr("TENANTS",tenantInfoService.getAll())
.addAttr("account",new UserAccountForm())
.toMav();
}
Here's the render library That I have created.
public class RenderMavBuilder {
private final ModelAndView mav;
public static RenderMavBuilder render(String viewname){
RenderMavBuilder target = new RenderMavBuilder(viewname);
return target;
}
public RenderMavBuilder addAttr(String attrName, Object value){
mav.addObject(attrName, value);
return this;
}
public RenderMavBuilder addAttr(Object value){
mav.addObject(value);
return this;
}
public RenderMavBuilder addAttrs(Map<String , ?> attrs){
mav.addAllObjects(attrs);
return this;
}
private RenderMavBuilder(String viewName){
this.mav = new ModelAndView(viewName);
}
public ModelAndView toMav(){
return mav;
}
}
Here's my validator
Here's my form.
<div class="col-md-6 centered">
<form:errors path="*" />
<form:form commandName="account" method="post" action="${pageContext.request.contextPath}/user/save">
<!-- Username -->
<div class="col-xs-12 form-group">
<label class="control-label">Username</label>
<form:input path="username" type="text" class="form-control" />
</div>
<!-- Password -->
<div class="col-xs-12 form-group">
<label class="control-label">Password</label>
<form:password path="password" class="form-control"/>
</div>
<!-- Password -->
<div class="col-xs-12 form-group">
<label class="control-label">Password</label>
<form:password path="retypedPassword" class="form-control"/>
</div>
<!-- First Name -->
<div class="col-xs-12 form-group">
<label class="control-label">First Name</label>
<form:input path="firstName" type="text" class="form-control"/>
</div>
<!-- First Name -->
<div class="col-xs-12 form-group">
<label class="control-label">Last Name</label>
<form:input path="lastName" type="text" class="form-control"/>
</div>
<!-- User Role -->
<div class="col-xs-12 form-group">
<label class="control-label">User Role</label>
<form:select path="accountRole" class="form-control">
<form:options items="${ACCOUNT_ROLES}"/>
</form:select>
</div>
<!-- Branch Designation -->
<div class="col-xs-12 form-group">
<label class="control-label">Designated Branch</label>
<select path="tenantId" items="${TENANTS}" class="form-control">
<c:forEach var="branch" items="${TENANTS}">
<option value="${branch.id}">${branch.tenantDescription}</option>
</c:forEach>
</select>
</div>
<!-- Button -->
<div class="col-md-12 form-group">
<button class="form-control btn btn-primary submit-button" type="submit">Save New User <i class="fa fa-check-square-o"></i></button>
</div>
</form:form>
On my controler the binding result has errors. however, the error is not being displayed on the view, what am I missing?
The problem is the way you are constructing your ModelAndView, you should use the Errors object to construct it. Use Errors.getModel() to obtain the underlying Map that represents the model and use that, next to the view name, to construct the ModelAndView.
Next to that for your other model attributes you should simply add #ModelAttribute annotated methods to retrieve those. I would also suggest adding a GET based method which you redirect to after successful submit.
Your helper class seems also rather pointless as doing it simply inside your controller would simplify your code (imho).
Your controller could look something along these lines.
#RequestMapping(value = "/save",method = RequestMethod.POST)
public ModelAndView saveUserAccount(#ModelAttribute("account") UserAccountForm userAccount, BindingResult result){
AddUserValidator userValidator = new AddUserValidator();
userValidator.validate(userAccount,result);
boolean hasErrors = result.hasErrors();
return new ModelAndView(hasErrors ? ADD_USER_VIEW, "redirect:/add", errors.getModel());
}
#RequestMapping(value="/add")
public String addUserAccount(Model model) {
model.addObject("account", new UserAccountForm());
return ADD_USER_VIEW;
}
#ModelAttribute
public void referenceData(Model model) {
model.addObject("ACCOUNT_ROLES", Arrays.asList(AccountRole.values()));
model.addObject("TENANTS",tenantInfoService.getAll());
}

Spring MVC Form Submit - Dynamically change the form catching Object

My Spring framework version 3.1.4
Question ??? Is there is any way to dynamically change the form catching Object depending on the some criteria.
Its really tough to explain, I will do my best here
JAVA OBJECTS
I have a Java Object PatientDocument.java
public class PatientDocument{
#Id
protected String documentId;
#Indexed
protected String patientId;
#Indexed
protected Integer documentType;
protected Object document;
}
The field document in above class can have any datatype objects depending upon the value in field documentType Eg : If documentType is 1, the Object representing the field 'document' will be MedicalCertificate.Java and I'm storing the PatientDocument into MongoDB collection.
MedicalCertificate.java looks like
public class MedicalCertificate {
protected String complaint;
protected String suggestedRestingDays;
protected Integer treatingDoctor;
protected Integer medicalDirector;
}
CLIENT SIDE
I'm using Thymeleaf for my client side rendering
My patientDocument.html looks like
<form action="#" id="patientDocument" th:action="#{/emr/patient/document/save}" th:object="${patientDocument}" method="post" class="form-horizontal">
<!-- #### HIDDEN FIELDS #### -->
<input type="hidden" th:field="*{documentId}" class="col-xs-12" readonly="readonly"/>
<input type="hidden" th:field="*{documentType}" class="col-xs-12" readonly="readonly"/>
<input type="hidden" th:field="*{patientId}" class="col-xs-12" readonly="readonly"/>
<!-- Medical Certificate -->
<section th:if="${patientDocument.documentType == 1}" layout:include="#{emr/patient/medicalCertificate} :: main"></section>
<!-- Referal Letter -->
<section th:if="${patientDocument.documentType == 2}" layout:include="#{emr/patient/referalLetter} :: main"></section>
<!-- Acknowledgment Form -->
<section th:if="${patientDocument.documentType == 3}" layout:include="#{emr/patient/acknowledgeForm} :: main"></section>
<form>
medicalCertificate.html looks like
<section layout:fragment="main">
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<label class="control-label col-xs-2">Complaint</label>
<div class="col-xs-10">
<textarea rows="5" th:field="*{document.complaint}" class="col-xs-12"></textarea>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-6">
<div class="form-group">
<label class="control-label col-xs-4">Rest For</label>
<div class="col-xs-8">
<input type="text" th:field="*{document.suggestedRestingDays}" class="col-xs-12"/>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-6">
<div class="form-group">
<label class="control-label col-xs-4">Treated By</label>
<div class="col-xs-8">
<input type="hidden" th:field="*{document.treatingDoctor}" readonly="readonly"/>
<input type="text" th:field="*{document.treatingDoctorName}" class="form-control"/>
</div>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label class="control-label col-xs-4">Medical Director</label>
<div class="col-xs-8">
<input type="hidden" th:field="*{document.medicalDirector}" readonly="readonly"/>
<input type="text" th:field="*{document.medicalDirectorName}" class="form-control"/>
</div>
</div>
</div>
</div>
</section>
View Controller
#RequestMapping(value="/document/save", method=RequestMethod.POST)
public String savePatientDocument(#ModelAttribute PatientDocument patientDocument, Model model, HttpServletRequest request){
logger.debug("Executing save for Patient Document : {}", patientDocument.toString());
////Logic to the Service Layer
}
Explanation of the Questions : As you can see the patientDocument.html the Client side form content corresponding to field 'document' will be replaced by Thymeleaf Fragments depending on documentType field. So when i submit the form into view controller the Object patientDocument contains the MedicalCertificate fields in place of field 'document'. Thats alright !!!
Now in View Controller, i need to say to the submit handler that "Hey, a PatientDocument.java object is coming as form submit. But the object inside the field 'document' will be 'MedicalCertificate.java'"
Where can i specify that?? Is there is any way to do it in SPRING MVC??
I need to alter the patientDocument.java Object as below before the Catching the client side form submit. But how?
PatientDocument patientDocument = new PatientDocument();
patientDocument.setDocument(new MedicalCertificate());
Thanks in advance
Good Day
Option 1: Change your container class to:
public class PatientDocument{
#Id
protected String documentId;
#Indexed
protected String patientId;
#Indexed
protected Integer documentType;
protected AcknowledgeForm acknowledgeForm;
protected MedicalCertificate medicalCertificate;
protected ReferalLetter referalLetter;
}
Then, form fields in medicalCertificate.html would look like:
<textarea rows="5" th:field="*{medicalCertificate.complaint}" class="col-xs-12"></textarea>
<input type="text" th:field="*{medicalCertificate.suggestedRestingDays}" class="col-xs-12"/>
<input type="hidden" th:field="*{medicalCertificate.treatingDoctor}" readonly="readonly"/>
<input type="text" th:field="*{medicalCertificate.treatingDoctorName}" class="form-control"/>
You will have to make similar changes to the other forms. Then, you can read the required property of PatientDocument based on its documentType.
Option 2: Write a PropertyEditor for PatientDocument to parse the request and set the document based on the request parameters.

Categories