On my website I have a few checkboxes each of them contains id in value attribute. After submitting form I'd like to have a list containing ids of checked checkboxes to be passed to the controller. That's how I want to make new page comparing n products.
Controller can accept List<Long> or long[]. That's what I have for now:
HTML:
<form th:action="#{/comparison}" th:object="${productsComparison}" target="_blank" method="post">
<table>
<tr data-th-each="item, iter : ${items.item}">
<td>
<input type="checkbox" th:name="|productsComparison.ids[${iter.index}]|" th:value="${item.id}"/>
</td>
</tr>
</table>
I've added to my controller List<Long> wrapped in ProductComparison with appropriate getters and setters. After submitting form list is always null.
Controller:
#RequestMapping("/productsPage")
public String showProducts(Model model) {
ProductsComparison productsComparison = new ProductsComparison();
model.addAttribute("productsComparison", productsComparison);
}
#RequestMapping("/comparison")
public String compareProducts(#ModelAttribute ProductsComparison productsComparison) {
System.out.println("List: " + productComparison.getIds());
// Always shows null
return "comparison";
}
public class ProductsComparison {
private List<Long> ids;
// Getters & setters
}
The underscores __ are ThymeLeaf's syntax for a preprocessing expression. Or in layman's terms, the stuff inside of the underscores are processed before the rest of the expression.
This is important because your expression is using an array, and the ${iter.index} portion of it gives the array index.
If you are curious or if anyone else stumbles upon this. th:name is no different than the html attribute name. The ThymeLeaf engine will just shove a name attribute into the html entity or overwrite the old one. ThymeLeaf has a ton of properties like this. th:field is a totally different beast though. It is what tells ThymeLeaf to bind the input of your form to the back forming bean or your model attribute, th:object="${productsComparison}". So with the expression th:field="*{ids[__${iter.index}__]}", Thymeleaf is going to call productsComparison.getIds[0] and the corresponding setter.
Note about th:name if you are submitting with Javascript of any kind, you probably want to use this. Most likely you are serializing your form, and the JSON created by this method call will use the name property.
If you want to know more about ThymeLeaf preprocessing expressions, there is a bit in the documentation about it.
I've finally found solution. Instead of th:name I had to use th:field.
th:field="*{ids[__${iter.index}__]}"
Because it's field I didn't have to specify object productsComparison before ids. ids field is List<String>.
To be honest I have no idea what underscores do, I will be glad if someone could explain.
Related
Newly started using thymeleaf for my application. I am building a dynamic form from controller where form fields are created as a list of map. Each key value pair is iterated as below.
<form th:action="#" th:object="${foo}" method="post">...
<input type="text" id=${element.key}
th:name=${element.key} th:value="${element.value}"/> ...</form>
My Pojo has properties in lower case as following java standard. But when building the key property my code uses the Capital letters from external file and also in order to have a better readability on UI not converting from upper case to lower case conversion in
mydata.put("KEY", 90);.
Upon submitting the page, my form elements not binding with propery key of Foo class due to KEY to key is not mapping. Getting null for the form fields due to input form field name as 'KEY'. I suspect the reason for getting null values for input text fields entered in html pages are due to case-sensitive of object properties in Foo class.
class Foo { private String key; //KEY can be a fix but naming convention is missing. }
Is there any thymeleaf tags available to ignore cases when binding html-elements to objects ? Or shall I use any JavaScript tweak upon submitting the form ? Like sending lower case and converting to upper case on form load to display ?
you can try to use ${#strings.toLowerCase(element.key)}
<form th:action="#" th:object="${foo}" method="post">...
<input type="text" th:id="${#strings.toLowerCase(element.key)}" th:name=${#strings.toLowerCase(element.key)} th:value="${element.value}"/> ...
</form>
I am trying to get my head into Thymeleaf and some form that are still quite simple, but am not able to get this to work...
I have a business object which stores some values that are classified by an enum (internal implementation is not in scope of this question):
public class MyBusObj {
public static enum MY_TYPE {A, B, C };
public int getValue( MY_TYPE type ) { ... }
public void setValue( MY_TYPE type, int value ) { ... }
}
When I pass an instance of that object to my model like
'model.addAttribute( "myBusObj", new MyBusObj() );'
it arrives at the template level and I can access all internally available values like this (showTypes is basically MY_TYPE or a subset of it as array):
<td th:each="type : ${showTypes}">
<input type="number" th:value="${myBusObj.getValue( type )}" th:field="${myBusObj.setValue( type )}"/>
</td>
It displays a line of edit boxes, one for each value. That's what I want to achieve and it's dynamic, for if I add more types, a new box for type 'D' will show up automatically, that's perfect.
While the direction from business object -> view works perfectly as 'getValue( type )' results in the correct value the part 'th:field="${myBusObj.setValue( type )}"' obviously can't work as it must be something like '(type, value)', but I can't write 'myBusObj.value( type )' either.
Can anyone think of a solution of how to write the user input back into the business object? Otherwise I must think of a "crutch" like restructure my business object.
Thanks in advance,
Stephan
If I understood correctly, you are trying to find a way to handle enum using Thymeleaf. The correct syntax to access enum values (say in a dropdown, for example) you need to use it like this:
<option th:each="myitem : ${T(your.package.YourEnum).values()}"
th:value="${myitem.name()}" th:text="${myitem.name()}"></option>
Similarly, you can do it in for-each for <td> like below:
<td th:each="type : ${T(your.package.YourEnum).values()}">
......
</td>
Hoping you got the idea. Take a look at here and here for a comprehensive example.
Update:
Got it. In that case you have to make use of rowStat provided by Thymeleaf where you can track the iteration.
But make sure you have the field in your bean as a Collection type "AND"
should have size matching your enum object.
Then you can do something like below:
<td> <select th:name="${yourbeanobj.beanFieldCollection[__${rowStat.index}__].beanFiled}" th:field="${yourbeanobj.beanFieldCollection[__${rowStat.index}__].beanFiled}" th:errorclass="is-invalid" multi>
<option value="abc">Iterate Your Enum List Here</option>
</select>
</td>
Not exact match for your requirement but you can take a look at a similar example here and here.
I am trying to implement client side validation in Struts 2. my theme is xhtml. The javascript generated is not able to validate my code. After debugging , I found that Struts is using the following notation to refer the elements.
form = document.getElementById(<form id>);
service = form.elements['service'];
the point is that service is coming as undefined.
when I checked that form.elements is null; However if I access form using document.formname i am able to see the fields in elements collection.
I am thinking document.forms[0] is returning the same object as document.getElementById(formid). What is the difference?
The form element can access fields by name, for this purpose you should get the form element. You can do it in many ways, use document.getElementById() or document.forms[], or $("#formid"). Whatever way you choose doesn't matter. Just note that a document can contain many forms, so you should reference a correct one. Getting form element by id returns an element that has an id attribute, getting it by the index in the forms property you should know the correct index. Once you get the form element you can reference input fields by name. For example
<form id="formid">
<input name="service">
</form>
<script>
var v = document.getElementById("formid")['service'];
</script>
My command object have a list of objects. I want to bind a text field to the attribute of the object inside that list. Is it possible to do in Spring MVC?
Command object class
public class SubDevisonDto {
private String devId;
private List subDevisions;
Subdevision object class mentioned in the list
public class SubDivison implements Serializable{
private String subDivisonName;
private String createdBy;
private String createdDate;
private String developerID;
private List users;
I want text box to set the value for subDivisonName field.
I have written the Spring MVC tags like this.
<spring:bind path="subdivisondto.subDevisions[0].subDivisonName">
<span class="formw">
<input name="subDivisonName" type="text" style="width:350px;" />
</span>
</spring:bind>
Just for test purpose I have given it as 0. If it's working I can make it to a variable. my requirement is, I should let the user to dynamically add subdevision objects. So, initially when page is loading I will just show one text box. I will give a button for him to add if he want to add more. I will dynamically generate text boxes when he clicks the add button. After that I have to submit the form with the list.
This jsp code gives me an error. It says:
org.springframework.beans.NullValueInNestedPathException
Is there anyway for me to do this in jsp code?
I found the answer for my question. But, it's not the solution for my requirement as I need to implement a dynamic list. but I found a solution for this question.
As I understood, first time we have to send data from back end to bind input elements. I didn't find a way to bind form elements which takes input without sending a list data from beck end. But when we send data and bind the elements, we can take input from those elements. So, I think to bind the element in a situation like this we need to send data first time. Correct me if this statement is wrong. Because, that would be a more good solution for me.
We need to use the lazy list and jsp code is bit modified.
Your command class object should be created as below mentioned.
import org.apache.commons.collections.list.LazyList;
import org.apache.commons.collections.FactoryUtils;
public class SubDevisonDto {
private String devId;
private List subDevisions =
LazyList.decorate(
new ArrayList(),
FactoryUtils.instantiateFactory(SubDivison.class));
JSP code should look like below.
<c:forEach items="${subs.subDevisions}" var="obj" varStatus="gridRow">
Binding an input element text box
<spring:bind path="subdivisondto.subDevisions[${gridRow.index}].subDivisonName">
<span class="formw"><input name="<c:out value="${status.expression}"/>" type="text" style="width:350px;" />
binding an input element check box. This input element makes a list.
<spring:bind path="subs.subDevisions[${gridRow.index}].users">
<c:forEach items="${obj.users}" var="dependenttwo" varStatus="dependentRowtwo">
<li>
<input name="<c:out value="${status.expression}"/>" type="checkbox" class="users" value="<c:out value="${dependenttwo}"/>"/>
<c:out value="${dependenttwo}"/>
</li>
</c:forEach>
</spring:bind>
`subs` is a map key name. the value for this key `subs` is a list of my DTO objects which named as `SubDevisonDto `
This code works fine for me.
Thanks the support given.
In dto :
private List<SubDivision> SubDivisions = new AutoPopulatingList<SubDivision>(new SubDivisionFactory());
and factory would be something like:
public class SubDivisionFactory implements AutoPopulatingList.ElementFactory<SubDivision> {
public String createElement(int index) {
SubDivision subDivision = new SubDivision();
return subDivision;
}
}
using AutopopulatingList from spring. And your jsp will look the same, you can iterate over as many as you want.
I'm building a spring mvc application.
Now the problem I have is the following.
I have a controller which adds a DayInfo[][] to my ModelMap.
(DayInfo has an id, a title (String) and Text(also String).
Now the problem I have is that I have no problem displaying this DayInfo[][] with <foreach> tags in my jsp.
However I'm outputting the Title property as an input box(type text), and I'd like to be able to update this value (and thus saving it to be a database but that shouldn't be a problem). However I'm having trouble with binding this value to the input box so that it is actually returned to the controller.
If anyone has some advice it would be welcome.
I have never done this with multidimensional arrays but it should be something like this (though I haven't tried it, it's just to give you an idea). In the JSP you should set the name of the input with each index, something like this:
<c:forEach var="row" items="${days}" varStatus="statusRow">
<c:forEach var="day" items="${row}" varStatus="statusCol">
<input type="text" name="days[${statusRow.index}][${statusCol.index}].title" value="${day.title}"/>
</c:forEach>
</c:forEach>
and in the controller you have to prepare days variable so the size of the array is the same as the one you get from the JSP. So you can use #ModelAttribute method to prepare the array (this method will be executed before the #RequestMapping method).
#ModelAttribute("days")
public getDays(){
DayInfo[][] days;
//Here you have to instantiate the days to prepare it so it can be filled
//You can load for example the data from the database
return days;
}
#RequestMapping("/yourURL")
public String getFormData(#ModelAttribute("days")DayInfo[][] days){
//Here in days you should have the data from the form overriding
// the one from the database
}
Hope this helps and sorry if there is any error, though I'm writing withoug trying it.