Not all of the Form validation errors shown in JSP - java

I have the following code for validation
#RequestMapping(value = "/itemValidation.json", method = RequestMethod.POST)
#ResponseBody
public ValidationResponse ajaxValidation(
#ModelAttribute(value = formName) #Valid Item item,
BindingResult result) {
ValidationResponse res = new ValidationResponse();
if (!result.hasErrors()) {
res.setStatus("SUCCESS");
} else {
res.setStatus("FAIL");
List<FieldError> allErrors = result.getFieldErrors();
List<ErrorMessage> errorMesages = new ArrayList<ErrorMessage>();
for (FieldError objectError : allErrors) {
errorMesages.add(new ErrorMessage(objectError.getField(),
objectError.getDefaultMessage()));
}
res.setErrorMessageList(errorMesages);
}
return res;
}
Upon validation there are three elements which do not satisfy the constraints as shown below:
The problem is on the JSP only the last two of the errors are shown. The error with fieldName : itemPK.name is not shown.
I use the below code to show the errors:
<span class="help-inline"><form:errors path="${name}" /></span>
My generated input elements in sequence:
<input id="itemPK.name_id" name="itemPK.name" type="text" value="">
<input id="price_id" name="price" type="number" value="">
<input id="point_id" name="point" type="number" value="">
Not sure what went wrong, hope anyone can shed some light on this.

If you said that last 2 field errors are displayed then you are probably have wrong name of the first field. Use
<form:input path="name" /><span class="help-inline"><form:errors path="name" /></span>
or use the path value itemPK.name that is reflect to your field name in the debug window.

I found out what the problem was . There's another layer where an ajax response method suppose to append all the error messages to the appropriate fields. The first error was never shown due to the jQuery selector unable to locate element with name itemPK.name, the fix was to change it to itemPK\\.name.

Related

Missing parameter error when submitting a form with two request params

Below is code from a controller that I'm aiming to make sure it's receiving two input parameters (name and code) from a front-end interface.
It's a page that takes two parameters within a submit form, "name" and "code".
#RequestMapping(method = RequestMethod.POST)
public String transfer(#RequestParam(name = "name") String name,
#RequestParam(name = "code") String code,
Errors errors, RedirectAttributes redirectAttributes) {
try {
User userToBeTransferred = usersRepository.findByName(name);
userToBeTransferred.setTransferred(true);
Region regionOfTransference = regionsRepository.findByCode(code);
regionOfTransference.setPopulationNumber(regionOfTransference.getPopulationNumber() + 1);
userToBeTransferred.setRegion(regionOfTransference);
usersRepository.save(userToBeTransferred);
regionsRepository.save(regionOfTransference);
return "redirect:/section/users/new";
} catch (IllegalArgumentException e) {
return "htmlPageOne";
}
}
The front-page form :
<form class="form-horizontal" method="POST" action="/section/users/new" th:object="${user}">
<input type="hidden" th:field="*{id}"/>
<div class="form-group row">
<label for="name" class="col-form-label">User name</label>
<input type="text" class="form-control" id="name" th:field="*{name}" name="name"/></div>
<div class="form-group row">
<label for="code" class="col-form-label">Code</label>
<input type="text" class="form-control" id="code" th:field="*{region.code}" name="code"/></div>
<button type="submit" class="btn btn-primary col-sm-6 ">Save</button>
</form>
For some reason, I'm getting the following error after I click to submit the form :
There was an unexpected error (type=Bad Request, status=400).
Required String parameter 'code' is not present
org.springframework.web.bind.MissingServletRequestParameterException: Required String parameter 'code' is not present
I'm not sure if I'm using the requestparams correctly, so maybe it's got something to do with this? I don't know, I've been stuck on this for a few hours now, so would appreciate if someone could help me.
The problem is that th:field="*{region.code}" overrides your name attribute into name="region.code". You can tell by inspecting the rendered <input> element using dev tools of your browser.
You can change following
th:field="*{region.code}"
...into:
th:value="${user.region.code}"

How to transfer data to controller as hash map from thymeleaf form

Please help me to solve this problem: I have no idea how to transfer data from thymeleaf view to controller, that expected data as a hash map? So let me explain in more detail.
I have next pojo, that is used as a wrapper for my hashMap data. It looks like this:
public class DTDays {
private Map<Driver, String> driversDays = new HashMap<>();
public Map<Driver, String> getDriversDays() {
return driversDays;
}
public void setDriversDays(Map<Driver, String> driversDays) {
this.driversDays = driversDays;
}
}
Controller has method with parameter model attribute and another pojo:
#RequestMapping(value = "/go", method = RequestMethod.POST)
public String advanced(#ModelAttribute DTDays dtDays,
#RequestParam(name = "tourId") Long tourId, Model model){
// make some business logic with provided data
return "redirect:/tours/advanced";
}
Here I debugged and dtDays is not null, but map property is empty, also tourId is worked as expected, I can get right value.
Now to the problem, view:
<body>
<div style="margin-left: 20px;">
<h1 th:text="${tour.tittle}"></h1>
<p>Add tour interval for drivers</p>
<form id="driverIntervals" th:action="#{/advanced/go}" th:object="${driversDays}" method="post">
<table>
<tr>
<td>Drivers:</td>
<td>Date:</td>
</tr>
<tr th:each="d: ${attachedDrivers}">
<td th:text="${d.id+' '+d.fullName}" >
<input type="hidden" th:value="${d.id}" >
</td>
<td>
<input type="text" placeholder="Pick days" th:name="days"/>
</td>
</tr>
</table>
<input type="hidden" th:name="tourId" th:value="${tour.id}"/>
<button type="submit">Confirm</button>
</form>
</div>
</body>
View looks like this:
What should i write in view to submit data? In my case Driver is a key of the map and user entered data in related input field will be a value of a map.
I already know how to submit List, by using select-option in a view:
<select th:name="drivers2attach" multiple="multiple" id="attachDrivers">
<!--/*#thymesVar id="drivers" type="java.util.List<stanislav.tun.novinomad.picasso.persistance.pojos.Driver>"*/-->
<option th:each="d : ${drivers}" th:value="${d.id}"
th:text="${d.getId()+' '+d.fullName}">
</option>
</select>
and #RequestParam List list in controller:
#RequestMapping(value = "/save", method = RequestMethod.POST)
public ModelAndView addTourAction(#ModelAttribute("tour") Tour tour,
#RequestParam(required = false, name = "drivers2attach") List<Long> drivers2attach)
But how to deal with map?
In case with list data is auto populated. In map only keys is prepopulated, this is count of drivers, and now i expect user input for each driver as a key value.
In researching of answers i already read these sources:
How to bind an object list with thymeleaf?
Send list object from thymeleaf to controller
How do I load HashMap and ModelandView object values using Thymeleaf in a Spring Boot application?
Thymeleaf Map Form Binding
http://forum.thymeleaf.org/How-to-use-Map-lt-String-String-gt-with-Spring-and-Thymeleaf-forms-td4028666.html
http://forum.thymeleaf.org/How-to-use-method-POST-with-a-complex-Hashmap-lt-Object-list-lt-Object-gt-gt-td4031257.html
Thymeleaf Map Form Binding
Use HashMap as Form Backing Bean Spring MVC + ThymeLeaf
etc.
But didn't helps. Somewhere in this links I found out that I should use some wrapper to do it, but again no idea why it not works, or what should I do additionally for make it working. Maybe I generally make wrong logic and to submit data as hashmap I shall convert data to list first and then somehow get map from it in controller?
Sorry for creating duplicate question, finally i found solution by following this answer on stackoverflow
i wrote little project for test it and it works. So i was on right way in controller with a wrapper. I missed only view map representation and syntax;
Finally view will looks like this:
view
here is view source code:
<form th:action="#{/save}" th:object="${form}" method="post">
<h1>Welcome</h1>
<div th:each="property : ${form.properties.entrySet()}">
<div class="form-group">
<label th:for="*{properties['__${property.key}__']}" th:text="${property.key}">Property</label>
<input type="text" class="form-control" th:field="*{properties['__${property.key}__']}" />
</div>
</div>
<button type="submit">send to controller</button>
and controller:
#PostMapping("/save")
public ModelAndView save(#ModelAttribute(name = "form") MapWrapper form){
var mav = new ModelAndView();
mav.setViewName("index");
mav.addObject("mapWrapper", form);
var map = form.getProperties();
System.out.println("Size of map = "+map.size());
for (Long id : map.keySet()) {
System.out.println("Key: "+id+"; Value: "+map.get(id));
}
return mav;
}
and output is :
output
P.S. reason of asking here is that i stuck on this problem for about 2 weeks, I despaired, but found solution after create a question.

Request method GET not supported when deleting

I have a table for adding one object to another and deleting one object from another.
This is my controller:
#Controller
#RequestMapping("/proj/{pid}/coupling/{r1}")
public class CouplingController {
#RequestMapping(method = RequestMethod.GET)
public String getAllCouplings( ){
return "riskCoupling";
}
#RequestMapping(value = "/{r1}", method = RequestMethod.POST)
public String saveCoupling( ){
return "/projects";
}
#RequestMapping(value = "/{r2}", method = RequestMethod.DELETE)
public String removeCoupling(){
return "/projects";
}
}
This is my Thymeleaf view
<td>
<form action = "#" th:action="#{/proj/{pid}/coupling/{r1}/{r2} (pid=${projectID},r1=${r1ID},r2=${r2.id})}" method = "post">
<input type="submit" name="Couple" value="Couple" class="btn btn-info" />
</form>
</td>
<td">
<form action = "#" th:action="#{/proj/{pid}/coupling/{r1}/{r2} (pid=${projectID},r1=${r1ID},r2=${r2.id})}" method = "delete">
<input type="submit" name="RemoveCoupling" value="RemoveCoupling" class="btn btn-info" />
</form>
</td>
When I go to the url /proj/{pid}/coupling/{r1} I get the overview so the GET works.
When I press the Couple button it works. So the POST works.
When I press the RemoveCoupling button I get the error:
Request method "Get" not supported.
I really don't know why I get this error.
Since browsers unfortunately do not support any other verbs than POST and GET in HTML forms, they will just send POST/GET requests instead. This might be the reason why only that one does not work.
See more:
http://martinfowler.com/articles/richardsonMaturityModel.html
http://amundsen.com/examples/put-delete-forms/
http://www.quora.com/HTTP/Why-are-PUT-and-DELETE-no-longer-supported-in-HTML5-forms
I fixed this with Thymeleaf - Button click to call http delete method.
Basically putting a hidden field in my form.

Displaying error on Play! 2 Framework

I am having big difficulties following Play! 2.2.x documentation and i am currently stuck on how to display error from my form validation.
This is my code:
Route
GET /account/create controllers.Account.create()
POST /account/create controllers.Account.createAccount()
Model
public static UserAccount create(UserAccount data){
UserAccount account = data;
String salt = BCrypt.gensalt();
account.email = data.email;
account.salt = salt;
account.hash = BCrypt.hashpw(data.hash, salt);
account.save();
return account;
}
Controller
// handles POST method
public static Result createAccount(){
Form<UserAccount> userForm = form(UserAccount.class).bindFromRequest();
if(userForm.hasErrors()){
return badRequest();
}else{
UserAccount.create(userForm.get());
Logger.info("Username is: " + userForm.get().email);
return ok("ok, I recived POST data. That's all...");
}
}
// Handles GET method
public static Result create(){
return ok(
views.html.account.form.render()
);
}
Views
#if(form.hasGlobalErrors) {
<p class="error">
#form.globalError.message
</p>
}
#helper.form(action = routes.Account.createAccount()) {
<input type="text" name="email" placeholder="Your Email Address"/><br/>
<input type="password" name="password" placeholder="Your Password"/><br/>
<input type="text" name="fname" placeholder="Your First Name"/><br/>
<input type="text" name="midname" placeholder="Your Middle Name"/><br/>
<input type="text" name="lname" placeholder="Your Last Name"/><br/>
<input type="text" name="dob" placeholder="Your Birthday"/><br/>
<select name="gender" id="gender">
<option value="1">Male</option>
<option value="2">Female</option>
<option value="3">Undecided</option>
</select><br/>
<input type="submit" value="Login" />
}
Error Message
value hasGlobalErrors is not a member of object views.html.account.form
Can anyone tell me what's wrong with my code? I am frustrated with the given example.
EDIT #1:
This is what i have done so far:
Models:
public static UserAccount create(UserAccount data){
UserAccount account = data;
String salt = BCrypt.gensalt();
account.email = data.email;
account.salt = salt;
account.hash = BCrypt.hashpw(data.hash, salt);
account.save();
return account;
}
Controllers:
// HANDLES GET REQUEST
public static Result create(){
return ok(
views.html.account.form.render(userForm)
);
}
// HANDLES POST REQUEST
public static Result createAccount(){
Form<UserAccount> userForm = form(UserAccount.class).bindFromRequest();
if(userForm.hasErrors()){
return badRequest(views.html.account.form.render(userForm));
}else{
// UserAccount.create(userForm.get());
// Logger.info("Username is: " + userForm.get().email);
UserAccount data = userForm.get();
return ok(data.email);
}
}
VIEWS/TEMPLATE
#(form: Form[UserAccount])
#if(form.hasGlobalErrors) {
<h1>Please fix the following error first</h1>
<p>
#form.globalError.message
</p>
<ul>
#for(error <- form.globalErrors) {
<li>#error.message</li>
}
</ul>
}
#helper.form(action = routes.Account.createAccount()) {
<input type="text" name="email" placeholder="Your Email Address"/><br/>
<input type="password" name="password" placeholder="Your Password"/><br/>
<input type="text" name="fname" placeholder="Your First Name"/><br/>
<input type="text" name="midname" placeholder="Your Middle Name"/><br/>
<input type="text" name="lname" placeholder="Your Last Name"/><br/>
<input type="text" name="dob" placeholder="Your Birthday"/><br/>
<select name="gender" id="gender">
<option value="1">Male</option>
<option value="2">Female</option>
<option value="3">Undecided</option>
</select><br/>
<input type="submit" value="Login" />
}
So far, according to firebug when i deliberately put errors on the form the server will return badrequest. However, no error is displayed by the template.
If i change the controller as such:
public static Result createAccount(){
Form<UserAccount> userForm = form(UserAccount.class).bindFromRequest();
if(userForm.hasErrors()){
return ok(userForm.errorsAsJson().toString());
}else{
// UserAccount.create(userForm.get());
// Logger.info("Username is: " + userForm.get().email);
UserAccount data = userForm.get();
return ok("ok, I received POST data. That's all...");
}
}
Or if i do this on my View/Template
<pre>#form.errorsAsJson.toString()</pre>
It works, and errors are printed accordingly. Do anyone know what i am missing here?
EDIT #2:
The best thing that works for me to output the error is by doing this on my View/Template
#(form: Form[UserAccount])
#if(form.hasErrors) {
<h1>Please fix the following error first</h1>
<ul>
#for(error <- form.errors) {
<li>#error.toString</li>
}
</ul>
}
Which outputs this:
(email,[ValidationError(email,error.required,[])])
(hash,[ValidationError(hash,error.required,[])])
Since I am trying to display an appropriate message to user, the message is rather useless.
After looking deeper at your code, there are two things to note:
In your template you need to use the helper tags for your form fields. The helpers will display field specific errors for you: http://www.playframework.com/documentation/2.2.x/JavaFormHelpers
I don't think your form has any global errors, which is why that code isn't displaying anything.
Try the helpers and see if that works for you.
You can use the flash scope:
Controller:
public Result create(){
flash("error", "error msg");
return badRequest(view.render());
}
View:
#flash.get("error")
The problem is that your form doesn't have "global errors", it has errors for the fields email and hash. It's different.
You can check the error for the fields using:
#if(form.error("email") != null) { }
And you can fire global error as belor on controller:
form.reject("global error");
More info:
http://www.playframework.com/documentation/2.0/JavaSessionFlash
I need to see your full template, controller, and routes file. I think the problem is that you don't have a method signature at the top of your template file, something like this:
#(userForm: Form[User])
Once you fix the template, you have another problem that you are not passing the form to the view template in the "badRequest()" flow. Try changing your controller action to look like this:
public static Result createAccount(){
Form<UserAccount> userForm = form(UserAccount.class).bindFromRequest();
if(userForm.hasErrors()){
return badRequest(views.html.createUserTemplate.render(userForm));
}else{
UserAccount.create(userForm.get());
Logger.info("Username is: " + userForm.get().email);
return ok("ok, I recived POST data. That's all...");
}
}
Your global errors code is mostly correct, it should look like this:
#if(userForm.hasGlobalErrors) {
<ul>
#for(error <- userForm.globalErrors) {
<li>#error.message</li>
}
</ul>
}

Play 2 - templates without helpers

#inputText(
signupForm("email"), '_label -> "Email",
'_help -> "Enter a valid email address."
)
How would I write this in pure html?
I have no idea how i add the value to the signupForm, so that I can use it in my controller with bindfromRequest() (in html)
Edit:
I normally used this approach
final static Form<User> signupForm = form(User.class);
and the the binding process
Form<User> filledForm = signupForm.bindFromRequest();
and my rendered form looks like this:
<div class="control-group ">
<label class="control-label" for="email">Email</label>
<div class="controls">
<input type="text" id="email" name="email" value="" >
<span class="help-inline"></span>
</div>
</div>
And this worked for me I was just curious how to use pure html, so I could create my own little helpers.
Edit2:
public static Result blank() {
return ok(form.render(signupForm));
}
and in the template itself
#(signupForm: Form[User])
Edit 3:
I don't know if this helps but the helper looks like this. (for the inputtext)
I just have no idea what this means, scala looks really cryptic to me.
#(field: play.api.data.Field, args: (Symbol,Any)*)(implicit handler: FieldConstructor, lang: play.api.i18n.Lang)
#input(field, args:_*) { (id, name, value, htmlArgs) =>
<input type="text" id="#id" name="#name" value="#value" #toHtmlArgs(htmlArgs)>
}
Use your browser to check the source coderendered by Play and copy/paste the HTML into your template.
In general the most interesting for you is proper inserting value to the manually created field:
<input type="text" name="email" value='#signupForm.field("email").value' />
It's also important to set the proper name attribut otherwise filling Form in controller will fail.
Edit:
Of course in blank action your signupForm is empty so that's normal that there's no value, in next action let's name it saveBlank, you need to fill the form with request's data, validate and save. If there are errors in form you need to render form.scala.view again with data binded to the form from previous request:
public static Result saveBlank(){
signupForm = signupForm.bindFromRequest();
if (signupForm.hasErrors()){
return badRequest(form.render(signupForm));
}
User user = new User();
user = signupForm.get();
user.save();
return ok("user saved);
}
of course if you'll want to edit existing user, you have to prefill it with data from DB ie:
public static Result edit(Long id){
signupform = signupForm.fill(User.find.byId(id));
return ok(form.render(signupForm));
}
(note: I wrote it just from top of my head, so check pls if there are no typos or errors)
Finally you don't need to use Form object in every case, you can also use DynamicForm:
public static Result saySomething(){
DynamicForm df = form().bindFromRequest();
return("you entered :" + df.get("email"));
}
or even in one line:
public static Result sayShorter(){
return("you entered :" + form().bindFromRequest().get("email"));
}

Categories