How to pass object from th:each loop to controller - java

I have form and corresponding controller to which list of objects is passed. After that this list is redirected as an attribute to another method and displayed in a website. After that I would like to pass object from this list via form in a carSearchResult.html file to controller. I was trying to do like it is described here Spring and Thymeleaf: Sending an object to a controller from a th:each table but it does not work properly. It is sending an object to controller but with fields property and model set as blank (but they are not null).
searchCar form
<form action="#" th:action="#{/user/searchCar}" method="post">
<input type="radio" name="type" value="hatch" /> Hatchback <br/>
<input type="radio" name="type" value="sedan" /> Sedan <br/>
<input type="radio" name="type" value="combi" /> Kombi <br/>
<input type="radio" name="type" value="sport" /> Sport <br/>
<input type="radio" name="type" value="cabrio" /> Cabrio <br/>
<input type="text" name="dailyPrice" placeholder="Price" /> <br/>
<input type="date" name="dateOfRent" placeholder="Date of rent" /> <br/>
<input type="date" name="dateOfReturn" placeholder="Date of return" /> <br/>
<input type="submit" value="Show" />
</form>
Controller method for searchCar post request
#PostMapping("/user/searchCar")
public String searchCar(Model model, #RequestParam String type, #RequestParam double dailyPrice,
#RequestParam Date dateOfRent, #RequestParam Date dateOfReturn, RedirectAttributes redirectAttr) {
List<Car> allCarsList = carDao.getAllCars(type, dailyPrice, dateOfRent, dateOfReturn);
redirectAttr.addFlashAttribute("carList", allCarsList);
return "redirect:/user/carSearchResults";
}
Method where list is redirected
#GetMapping("/user/carSearchResults")
public String showCarSearchResults(Model model) {
model.addAttribute("car", new Car());
return "user/carSearchResults";
}
Form where list is displayed
<div th:each="car: ${carList}">
<h3 th:text="${car.producer}"></h3>
<h3 th:text="${car.model}"></h3>
<form action="#" th:action="#{/user/showCarDetails}" method="post" th:object="${car}">
<input type="hidden" th:field="*{producer}" /> <br/>
<input type="hidden" th:field="*{model}" /> <br/>
<input type="submit" value="Show" />
</form>
</div>
Method to which I want to send object from th:each loop
#PostMapping("/user/showCarDetails")
public String showCarDetails(#ModelAttribute Car car) {
System.out.println(car);
return "user/showCarDetails";
}
After printing information about car in console I receive object below. It looks like it is passed but with parameters as a blank space. Can anyone help me? Many thanks.
Car [id=null, producer=, model=, seatsNumber=null, type=null, registrationNumber=null, dailyPrice=null, description=null]
EDIT
I have changed the form. Option with th:attr and th:value works but I do not really understand why th:field does not work and what is the difference between them. As I understand from documentation th:value is some kind of older version of th:field?
<div th:each="car: ${carList}" th:object="${car}">
<h3 th:text="*{producer}"></h3>
<h3 th:text="*{model}"></h3>
<form action="#" th:action="#{/user/showCarDetails}" method="post">
<input type="hidden" th:value="*{producer}" name="producer" /> <br/> <!-- It works -->
<input type="hidden" th:attr="value=*{producer}" name="producer" /> <br/> <!-- It works -->
<input type="hidden" th:field="*{producer}" /> <!-- It does not work -->
<input type="submit" value="Pokaż" />
</form>
</div>

th:object works differently when you use it on a <form>, vs. when you use it other places. When you use it in a form, it expects it to resolve to a single variable on the model. You cannot use it with a temporary variable (like {$car} -- which isn't on the model, it's just a temporary variable created byth:each) because the attribute just isn't built to work like that.
th:field generates both the value and name tags -- but again, it has to work within the same limitations as th:object. The reason manually adding name and value attributes work, is because they happen to line up with what spring is expecting in a post and it's able to create the object based on the names.
As for how to solve the original problem... I think you have two options:
Create a command object which has a List of Cars as a property. That way you can use the th:object and th:field properties. You'll have to have the submit button to supply which car is being edited.
Continue manually creating the name and value fields like you already are.

Related

JSP FORM POST : empty attributes

I can't find one attribute sent from a JSP form.
<form name="shuntingForm" action="/TCS_FUND/ShuntAction.aen" method="POST">
<div class="field-item clearfix">
<input type="hidden" name="csrfPreventionSalt" value="<%=CsrfPreventionSalt%>" />
<input type="hidden" name="idSession" value="<%=idSession%>">
Funds Trading<br>
</div>
</form>
request.getAttribute("idSession") & request.getParameter("idSession") show NULL.
Any ideas ? thanks
I would simplify this to return a fixed value.
Change:
<input type="hidden" name="idSession" value="<%=idSession%>">
To:
<input type="hidden" name="idSession" value="123456789">
Once that works, figure out the difference.
You also may want to view the source of the web page. If your idSession does not have the value you expect, your input tag will show the wrong value field.

Why missing <fieldset> tag will cause controller cannot get form value?

I am developing a WebApp which uses Spring-MVC & Thymeleaf as template engine.
In my HTML,I got a form like this
<form action="/submit" method="POST">
First name: <input type="text" name="fname"/>
Last name: <input type="text" name="lname"/>
<input type="text" id="taskId" name="taskId" th:value="${taskId}" />
<input type="submit" value="Submit"/>
</form>
And I have a controller
#RequestMapping("/submit")
public void addUser(String taskId, String fname, String lname, ModelMap map) {
//save to db...
}
At first I expect taskId, fname, lname will contains the value from the form, but they are all null
Finally I try to wrap all the input field by <fieldset>tag, and it suddenly works. i.e:
<form action="/submit" method="POST">
<fieldset>
First name: <input type="text" name="fname"/>
Last name: <input type="text" name="lname"/>
<input type="text" id="taskId" name="taskId" th:value="${taskId}" />
<input type="submit" value="Submit"/>
</fieldset>
</form>
I would like to know:
Why adding <fieldset> will make everything works?
The HTML - <fieldset> tag is used to group related elements in a form.
Basically the <fieldset> tag draws a box around the related elements in a form.
Most online forms are poorly designed, disorganized, and hard to use. One thing you can do to make your forms better is to organize them into logical sections. The <fieldset> element provides an easy way to break forms down into logical sections.
For more information you can always got to - https://html.com/tags/fieldset/

Thymeleaf insert text in html code?

I want to insert an Attribute into the html Code.
I tried this, but it's not working:
<div id="${var}"> ... </div>
I think you know what I mean. The attribute 'var' should be the id. I didn't find a solution...
You just need to use the th:attr attribute. It is explained in the reference documentation 5.1:
5.1 Setting the value of any attribute
Enter then the th:attr attribute, and its ability to change the value
of attributes of the tags it is set in:
<form action="subscribe.html" th:attr="action=#{/subscribe}">
<fieldset>
<input type="text" name="email" />
<input type="submit" value="Subscribe!" th:attr="value=#{subscribe.submit}"/>
</fieldset>
</form>
The
concept is quite straightforward: th:attr simply takes an expression
that assigns a value to an attribute. Having created the corresponding
controller and messages files, the result of processing this file will
be:
<form action="/gtvg/subscribe">
<fieldset>
<input type="text" name="email" />
<input type="submit" value="¡Suscríbe!"/>
</fieldset>
</form>
Use this
<div th:attr="id=${var}"> ... </div>
Thymeleaf only evaluates attributes that are prefixed with th:. Here is a list of the attributes that are evaluated:
http://www.thymeleaf.org/doc/tutorials/2.1/usingthymeleaf.html#setting-value-to-specific-attributes
In your case, th:id is already built in so you can simply do <div th:id="${var}"> ... </div> and it will work. th:attr, is used to define attributes that thymeleaf doesn't normally support.

How to pass a object from a jsp to another using EL to get the object in the second jsp

I am passing an object from page1.jsp using
<form action="teste-altera-mvc.jsp" method="get">
<input type="hidden" value="${contato}" name="contato" />
<input type="submit" value="Alterar" />
</form>
Contato is my object with 4 properties. My problem is how can i get this object in "teste-altera-mvc.jsp" and display its properties using expression language

Form values are null in post request in play framework

I have html to post back a form, like this:
#(message: String)
#main("Contact") {
<link rel="stylesheet" media="screen" href="#routes.Assets.at("stylesheets/contact.css")">
<div id="pageContainer">
<div id="form">
<div id="topText">
<p>Have a question or need some assistance? Let us know and we'll get back to you ASAP.</p>
</div>
<br/>
<form method="POST" action="#routes.Home.contact()">
<div id="contactInfo">
<label class="contactLabel">First Name:</label><input type="text" name="firstName" id="firstName" class="contactInput" /> <br />
<label class="contactLabel">Last Name:</label><input type="text" name="lastName" id="lastName" class="contactInput" /> <br />
<label class="contactLabel">Email:</label><input type="text" name="email" id="email" firstName" class="contactInput" /> <br />
<label class="contactLabel">Company:</label><input type="text" id="company" name="company" class="contactInput" /> <br />
<input type="hidden" id="hidden" name = "hidden"></input>
<p id="crap">#message</p>
</div>
<br/>
<div id="message">
<label class="contactLabel">Message:</label><textarea cols="50" rows="10"></textarea>
</div>
<input type="submit" name="submit" id="submit" value="Submit"></input>
</form>
</div>
</div>
}
Back in the controller, it looks like this:
public static Result contact()
{
//Map<String,String[]> values = request().body();
DynamicForm form = form().bindFromRequest();
String first = "";
if(form.data().get("firstName") != null)
first = form.data().get("firstName").toString();
return ok(views.html.contact.render(first));
}
But when I look through the result of form().bindFromRequest(), it is always empty. Am I missing something?
Here is how I would do that:
template
#(message: String, contactForm: Form[Contact])
#import helper._
#main("Contact") {
<link rel="stylesheet" media="screen" href="#routes.Assets.at("stylesheets/contact.css")">
<div id="pageContainer">
<div id="form">
<div id="topText">
<p>Have a question or need some assistance? Let us know and we'll get back to you ASAP.</p>
</div>
<br/>
#form(routes.Home.contact()) {
<div id="contactInfo">
<label class="contactLabel">First Name:</label> #inputText(contactForm("firstName"), 'class -> "contactInput"') <br />
<label class="contactLabel">Last Name:</label> #inputText(contactForm("lastName"), 'class -> "contactInput"') <br />
<label class="contactLabel">Email:</label> #inputText(contactForm("email"), 'class -> "contactInput"') <br />
<label class="contactLabel">Company:</label> #inputText(contactForm("company"), 'class -> "contactInput"') <br />
<input type="hidden" id="hidden" name="hidden"></input>
<p id="crap">#message</p>
</div>
<br/>
<div id="message">
<label class="contactLabel">Message:</label><textarea cols="50" rows="10"></textarea>
</div>
<input type="submit" value="Submit" />
}
</div>
</div>
}
Class Contact
public class Contact {
public String firstName;
public String lastName;
public String email;
public String company;
// other stuff
}
Controller
public static Result contact() {
Form<Contact> contactForm = form(Contact.class).bindFromRequest();
if (contactForm.hasErrors()) {
// process
} else {
// contactForm.get().firstName should be filled with the correct data
return ok(views.html.contact.render("message", contactForm));
}
}
Does it work for you ?
You should take a look at the Play2 documentation about these topics:
JavaForms
JavaForm Helpers
Edit
if you can explain why the way I was doing it previously didn't work
I'm not sure about that, but I can see one problem in your code, you don't have to call data() on the dynamic form, using directly DynamicForm.get() is sufficient, so first = form.get("firstName") is sufficient.
Besides as you can see from the DynamicForm Javadoc
get(java.lang.String key)
Gets the concrete value if the submission was a success.
The internal map behind DynamicForm has values if there is no errors, so you may check with hasErrors before actually getting concrete values from field.
From my point of view, it is better and easier to use the form(Contact.class).bindFromRequest() style that will fill an instance of class Contact, this without saying that fields can be validated using Java annotations.
You must provide a Request to bindFromRequest() method. For example
DynamicForm form = form().bindFromRequest(request());
The request function provides the current Request for the current action.
Seems like bindFromRequest doesn't bind automatically the current Request
I was having the same problem and google brought me here, hope it helps.
Maybe not exactly the same problem but on my Windows developing Play 2.6 app I had similar issue. Binding did not work. I mean the Form's data field had proper values, but the values of model were empty. That was causing No such element exception: No value present. I've spent quite a while looking into this. What fixed the issue was cleaning project, recompiling and running it again. Ehh.

Categories