JSP doesn't display validation errors - spring

<spring:bind path="aDepartment">
<div class="form-group ${status.error ? 'has-error' : ''}">
<label class="col-lg-4 control-label">Department *</label>
<div class="col-lg-8">
<form:input class="form-control" path="aDepartment" required="true"
placeholder="Department" />
<form:errors path="aDepartment" class="control-label" />
</div>
</div>
</spring:bind>
PATH is the path of the current page.
When I violate validation rules (on purpose) , errors are not bound to the model and the jsp doesn't display the errors.
Should I bind the errors manually to the model ? If so how can I do it ?
#RequestMapping(value = "/admin/adepartement/add", method = RequestMethod.POST)
public String add(
#ModelAttribute(value = "addadepartment") #Valid ADepartment pADepartment,
final BindingResult pBindingResult, final ModelMap pModel) {
if (!pBindingResult.hasErrors()) {
///
}
}else{
return PATH;
}
}

It should work just fine !
Make sure that "aDepartment" in the path refers to an attribute called "aDepartment" in your "ADepartment" Class.

Related

Spring - BindingResult

I don`t know why, when I POST the empty form I have that error
org.springframework.expression.spel.SpelEvaluationException: EL1007E: Property or field 'addresses' cannot be found on null
before I POST the form:
#GetMapping("/new")
public String showCreateFormForEmployeeAndAddresses(Model model) {
if(addressService.getCheck()== false) {
for (int i = 1; i <= 2; i++) {
addressService.newAddress(new Address());
}
}
model.addAttribute("employee", new Employee()).addAttribute("form", addressService);
return "new_employee_form";
}
After I POST the empty form: (in filled form everything work fine)
#RequestMapping(value = "/employees", method = RequestMethod.POST)
public String saveEmployeeAndAddress(#ModelAttribute #Valid Employee employee,
BindingResult bindingResultEmployee,
#ModelAttribute #Valid AddressRepository addresses,
BindingResult bindingResultAddressRepository, Model model) {
if(bindingResultEmployee.hasErrors() || bindingResultAddressRepository.hasErrors()) {
return "new_employee_form";
} else{
ExecutorService executor = Executors.newSingleThreadExecutor();
Runnable runnableEmployee = () -> employeeService.saveEmployeeToDB(employee);
List<Address> addressesFromForm = addresses.getAddresses();
Runnable runnableAddress = () -> addressService.saveAddressToDB(addressesFromForm);
executor.submit(runnableEmployee);
executor.submit(runnableAddress);
return "redirect:/employees";
}
}
It show after this line: return "new_employee_form";
In Console I have also:
[THYMELEAF][http-nio-8080-exec-2] Exception processing template "new_employee_form": Exception evaluating SpringEL expression: "addresses" (template: "new_employee_form" - line 57, col 30)
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1007E: Property or field 'addresses' cannot be found on null
and the view:
<div th:each="address, stat : *{addresses}">
<span th:switch="*{addresses[__${stat.index}__].type}">
<div style = "text-align: center;" th:case="P" ><h5>Adres stały</h5></div>
<div style = "text-align: center;" th:case="C" ><h5>Adres korespondencyjny</h5></div>
</span>
<div class="form-group">
<input type="hidden" class="form-control" th:field="*{addresses[__${stat.index}__].type}"/>
</div>
<div class="form-group">
<input type="text" class="form-control" th:field="*{addresses[__${stat.index}__].street}"/>
<label class="control-label">Ulica</label>
<div class="text-danger"><p th:if="${#fields.hasErrors('addresses[__${stat.index}__].street')}" th:errors="*{addresses[__${stat.index}__].street}"/></div>
</div>
<div class="form-group">
<input type="text" class="form-control" th:field="*{addresses[__${stat.index}__].streetNr}"/>
<label class="control-label">Numer domu</label>
<div class="text-danger"><p th:if="${#fields.hasErrors('addresses[__${stat.index}__].streetNr')}" th:errors="*{addresses[__${stat.index}__].streetNr}"/></div>
</div>
<div class="form-group">
<input type="number" class="form-control" th:field="*{addresses[__${stat.index}__].flatNr}"/>
<label class="control-label">Numer mieszkania</label>
<div class="text-danger"><p th:if="${#fields.hasErrors('addresses[__${stat.index}__].flatNr')}" th:errors="*{addresses[__${stat.index}__].flatNr}"/></div>
</div>
<div class="form-group">
<input type="text" class="form-control" th:field="*{addresses[__${stat.index}__].postalCode}"/>
<label class="control-label">Kod pocztowy</label>
<div class="text-danger"><p th:if="${#fields.hasErrors('addresses[__${stat.index}__].postalCode')}" th:errors="*{addresses[__${stat.index}__].postalCode}"/></div>
</div>
<div class="form-group">
<input type="text" class="form-control" th:field="*{addresses[__${stat.index}__].city}"/>
<label class="control-label">Miasto</label>
<div class="text-danger"><p th:if="${#fields.hasErrors('addresses[__${stat.index}__].city')}" th:errors="*{addresses[__${stat.index}__].city}"/></div>
</div>
<div class="form-group">
<input type="text" class="form-control" th:field="*{addresses[__${stat.index}__].country}"/>
<label class="control-label">Kraj</label>
<div class="text-danger"><p th:if="${#fields.hasErrors('addresses[__${stat.index}__].country')}" th:errors="*{addresses[__${stat.index}__].country}"/></div>
</div>
</div>
Returned view new_employee_form requires an addresses attribute to be supplied to it.
It looks like #ModelAttribute #Valid AddressRepository addresses argument is null, so it has to be provided manually:
if(bindingResultEmployee.hasErrors() || bindingResultAddressRepository.hasErrors()) {
model.addAtribute("addresses", an_instance_of_whatever_addresses_is)
return "new_employee_form";
}
Looking at the given code, I would expect that the Getter method (showCreateFormForEmployeeAndAddresses(Model model)) provides it to the model (otherwise it's validation would always null-fail).
In any case, I would highly recommend you to reevaluate the way you name your model attribute classes and variables, as names like AddressRepository or addressService have totally different meaning in Spring MVC.

Thymeleaf Could not bind form errors using expression "*"

I use Spring-boot and Thymeleaf template engine and I try use th:classappend attribute for adding optional "has-error" class for < div > html tag using #fields.hasErrors('*') expression
<form method="POST" action="/registration" class="form-signin">
<h2 class="form-signin-heading">Create your account</h2>
<div class="form-group" th:classappend="${#fields.hasErrors('*')} ? 'has-error' : ''">
<input name="username" type="text" class="form-control" placeholder="Username" autofocus="true"/>
<p class="alert alert-danger" th:if="${#fields.hasErrors('username')}" th:errors="*{username}"></p>
</div>
<div class="form-group" th:classappend="${#fields.hasErrors('*')} ? 'has-error' : ''">
<input name="password" type="text" class="form-control" placeholder="Password" autofocus="true"/>
<p class="alert alert-danger" th:if="${#fields.hasErrors('password')}" th:errors="*{password}"></p>
</div>
<div class="form-group" th:classappend="${#fields.hasErrors('*')} ? 'has-error' : ''">
<input name="passwordConfirm" type="text" class="form-control" placeholder="Confirm your password" autofocus="true"/>
<p class="alert alert-danger" th:if="${#fields.hasErrors('passwordConfirm')}" th:errors="*{passwordConfirm}"></p>
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit">Submit</button>
</form>
but I have this error
Could not bind form errors using expression "*". Please check this
expression is being executed inside the adequate context (e.g. a
with a th:object attribute)
my controller methods
#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("userForm") 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:/welcome";
}
what am I doing wrong?
I just add th:object="${userForm} attribute to my form element. And now it work fine!

Spring4 + Thymeleaf3 Form Validation : bean name #fields not available in templates

I get the below error in my spring4 + thymeleaf3 application when I try to show validation errors in my form template.
Neither BindingResult nor plain target object for bean name '#fields' available as request attribute
My form is as below.
<form th:action="#{/user/save}" method="post" th:object="${user}">
<ul th:if="${#fields.hasErrors()}">
<li th:each="err : ${#fields.errors('*')}" th:text="${err}"></li>
</ul>
<div>
<label>Name</label>
<div>
<input type="text" th:field="*{firstName}" placeholder="First Name">
<input type="text" th:field="*{lastName}" placeholder="Last Name">
<div th:if="${#fields.hasErrors('firstName')}" th:errors="${#fields.errors('firstName')}"></div>
<div th:if="${#fields.hasErrors('lastName')}" th:errors="${#fields.errors('lastName')}"></div>
</div>
</div>...
The form is rendered well for the following get request mapping.
#GetMapping("/create")
public String create(ModelMap model) {
model.put("user", new User());
return VIEW_DIR.concat("form");
}
But it gives the above error when the form is submitted with some invalid fields to the following method.
#PostMapping("/save")
public String save(#Valid User user, BindingResult bindingResult, ModelMap model) {
if(bindingResult.hasErrors()) {
return VIEW_DIR.concat("form");
}
userService.save(user);
return "redirect:list";
}
Can you please show me where the error is.
You are setting wrong values for th:errors inside your form element div. th:errors should contain field name. Update your form with this:
<div>
<input type="text" th:field="*{firstName}" placeholder="First Name">
<input type="text" th:field="*{lastName}" placeholder="Last Name">
<div th:if="${#fields.hasErrors('firstName')}" th:errors="*{firstName}"></div>
<div th:if="${#fields.hasErrors('lastName')}" th:errors="*{lastName}"></div>
</div>

form:error not showing error

When validating object controller just refreshes the page, instead showing errors via form:errors, can you tell me where is the problem. I guest it needs to get error from binding result and insert it into the page, instead controller is just refreshing page
Controller :
#PreAuthorize("hasRole('ROLE_ADMIN')")
#RequestMapping(value = "/create", method = RequestMethod.POST)
public String createProduct(MultipartFile image, Model model,#ModelAttribute #Valid ProductDto product, BindingResult bindingResult) throws IOException {
productValidator.validate(product,bindingResult);
if (bindingResult.hasErrors()){
return "admin/create";
} else {
return "index";
}
}
#PreAuthorize("hasRole('ROLE_ADMIN')")
#RequestMapping(value = "/create", method = RequestMethod.GET)
public String createProduct(Model model) {
model.addAttribute("product", new ProductDto());
model.addAttribute("categories", categoryService.findAll());
return "admin/create";
}
Validator :
#Override
public boolean supports(Class<?> aClass) {
return ProductDto.class.equals(aClass);
}
#Override
public void validate(Object o, Errors errors) {
ProductDto product = (ProductDto) o;
if (product.getTitle().isEmpty() || product.getTitle() == null) {
errors.rejectValue("title", "product.title", "Product title cant be empty");
}
if (product.getDescription().isEmpty() || product.getDescription() == null) {
errors.rejectValue("description", "product.description", "Product description cant be empty");
}
if (product.getPrice() == null || product.getPrice()<=0) {
errors.rejectValue("price", "product.price", "Product price is not valid");
}
if (product.getCategoryId()==null) {
errors.rejectValue("category", "product.category", "Product category is not valid");
}
}
jstl page :
<spring:url value="/product/create" var="formUrl"/>
<form:form modelAttribute="product" action="${formUrl }" method="post" enctype="multipart/form-data">
<div class="form-group">
<label>Title</label>
<form:input id="title"
cssClass="form-control" path="title"/>
<form:errors path="title"/>
</div>
<div class="form-group">
<label>Description</label>
<form:textarea id="description" rows="10"
cssClass="form-control" path="description"/>
</div>
<div class="form-group">
<label>Price</label>
<form:input id="price" type="number"
cssClass="form-control" path="price"/>
<form:errors path="description"/>
</div>
<div class="form-group">
<label for="sel1">Select category</label>
<form:select id="sel1" cssClass="form-control" path="categoryId">
<form:options items="${categories}" itemValue="id" itemLabel="title"/>
</form:select>
</div>
<label class="btn btn-default btn-file">
Image <input type="file" multiple accept='image/*' ng-file-select="onFileSelect($files)" name="image" style="display: block;">
</label>
<br><br>
<div class="text-center">
<button type="submit" class="btn btn-lg btn-success text-center"><span
class="fa fa-check"></span> Submit
</button>
<a href="/" class="btn btn-danger btn-lg text-center"><span
class="fa fa-times"></span> Cancel
</a>
</div>
</form:form>
I think your product model attribute has different name in your POST-handling method.
According to documentation:
The default model attribute name is inferred from the declared
attribute type (i.e. the method parameter type or method return type),
based on the non-qualified class name: e.g. "orderAddress" for class
"mypackage.OrderAddress", or "orderAddressList" for
"List<mypackage.OrderAddress>".
So your ProductDto product attribute will have name productDto in resulting model. But you referring the product in your template.
Try to explicitly set the name attribute of #ModelAttribute annotation
public String createProduct(MultipartFile image, Model model, #ModelAttribute(name = "product") #Valid ProductDto product, BindingResult bindingResult)
And to make sure that bidning and validation actually works, try to log it:
if (bindingResult.hasErrors()) {
System.err.println("Form has errors!");
}

spring RequestMapping not working without ModelAttribute

I have controller class with following request mapping method.
appStart() method is responsible for redirecting user to login.html and
logout() is responsible for invalidating session and redirecting user
back to login.jsp
if I remove #ModelAttribute from their parameter then these two methods are throwing exception, is there any hack to get these methods working without modelattribute?
controller methods.
#RequestMapping(value="/",method=RequestMethod.GET)
public String appStart(#ModelAttribute("tempAdmin") Admin tempAdmin) {
return "login.jsp";
}
#RequestMapping(method = RequestMethod.POST,name="doLogin")
public ModelAndView doLogin(#ModelAttribute("tempAdmin") Admin tempAdmin, HttpServletRequest request) {
ModelAndView mvc = new ModelAndView();
/*
Buisness logic
*/
mvc.setViewName("home.jsp");
return mvc;
}
#RequestMapping("doLogout")
public String logout(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if(session != null){
session.invalidate();
}
return "login.jsp";
}
login.jsp
<form:form action="doLogin" modelAttribute="tempAdmin" cssClass="form-horizontal">
<div class="form-group">
<label for="username" class="col-sm-2 control-label">Username</label>
<div class="col-sm-10">
<form:input cssClass="form-control" path="adminId" placeholder="username" />
</div>
</div>
<div class="form-group">
<label for="passwd" class="col-sm-2 control-label">Password</label>
<div class="col-sm-10">
<form:password path="password" cssClass="form-control" id="passwd" placeholder="password" />
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default">Sign in</button>
</div>
</div>
</form:form>
stacktrace.
Caused by: java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'tempAdmin' available as request attribute
at org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:144)
at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getBindStatus(AbstractDataBoundFormElementTag.java:168)
at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getPropertyPath(AbstractDataBoundFormElementTag.java:188)
I will tell you how to change your controller, to avoid binding result problem. Try this :
#RequestMapping(method = RequestMethod.POST,name="doLogin")
public String doLogin(#ModelAttribute("tempAdmin") Admin tempAdmin, HttpServletRequest request,Model model) {
model.addAttribute("tempadmin",new Admin());
// business logic
return "home";
}
Try this out, and if you have any other classes, then add the model.addAttribute for that as well. Can you post your JSP too?

Resources