Thymeleaf select dropdown not populating full Java object - spring-mvc

I have the following code with Thymeleaf and Spring. For some lists that I have the value of the selected option populates the entire object however for some it does not.
public class BeneficiaryUploadCommand {
private List<SchemeCommand> schemeCommandList;
private List<BudgetHeads> budgetHeadsList;
#NotEmpty
private List<BeneficiaryType> beneficiaryTypeLists;
#NotEmpty
private List<FinancialYear> financialYearList;
#NotEmpty
private List<SubSchemes> subSchemesList;
private Date toDate;
private Date fromDate;
#Size(min=10, max = 10)
private String toDate1;
#Size(min=10, max = 10)
private String fromDate1;
#NotEmpty
private List<BenefitType> benefitTypesList;
#NotNull
private Integer beneficiariesProposed;
#NotNull
private Double stateShare;
private Double actualExpenditure;
#NotNull
private Double advancedExpenditure;
#NotNull
private char aadharLinkedOrNot;
#NotNull
private char cropItemDataAvailable;
#NotNull
private String schemeCommandId;
#NotNull
private String budgetHeadsListId;
}
Thymeleaf code is as follows:
<select id="financialYearListId" th:field="*{financialYearList}" style="width:100px; float:left;" >
<option th:value="0" th:text=" Select "></option>
<th:block th:each="finYear : ${beneficiaryData.financialYearList}">
<option th:value="${finYear.id}" th:text="${finYear.financialYear}" label=" - Select - "></option>
</th:block>
</select>
</td>
<td colspan="1" align="right"><font color="red">*</font>
<b>From Date</b>
</td>
<td colspan="1">
<input type="text" th:field="*{fromDate1}" name="from_date" class="date form-control" style="width: 100px; margin: 0px;"/>
</td>
<td colspan="1" align="right"><font color="red">*</font>
<b>To Date</b>
</td>
<td colspan="1">
<input type="text" th:field="*{toDate1}" name="to_date" class="date form-control" style="width: 100px; margin: 0px;"/>
</td>
</tr>
<tr>
<td colspan="1"><font color="red">*</font> <b>Scheme</b></td>
<td colspan="1" width="20%">
<select th:field="*{schemeCommandId}" name="scheme_id" style="width:250px" th:onchange="'getSubSchemesandBudgetHeads(this.value);'">
<th:block th:each="scheme : ${beneficiaryData.schemeCommandList}">
<option th:value="${scheme.id}" th:text="${scheme.schemeName}" label=" - Select - " />
</th:block>
</select>
Now when I select the th:field="*{financialYearList}"
the entire financialyear object of the List object is populated. However with schemeCommandList it only gives the id? I am confused how can some objects be converted whole whereas some it only provides the id.

The difference seems to be that, for the 'financial year' dropdown, your Java class has:
private List<FinancialYear> financialYearList;
which corresponds to the following Thymeleaf markup:
<select id="financialYearListId" th:field="*{financialYearList}"...
However, the 'scheme command' dropdown, has the following markup:
<select th:field="*{schemeCommandId}"...
which is populating the Java field:
private String schemeCommandId;
In other words, the Thymeleaf markup is telling the first dropdown to populate the financialYearList field, where the second one is only telling it to populate the schemeCommandId field, and that seems to be the effect you're seeing.
So, I imagine the solution to get what you want would be to change the Thymeleaf markup for the second dropdown to be:
<select th:field="*{schemeCommandList}"...

Related

How to display properties of specific object selected via dropdown?

I wrote a query in which I get the semester class, display its values ​​as a name and get its id. Accordingly, when I select the semester I need in the drop-down list, I am given the semester that I chose. But I also need to display its duration in another field, I can do this if I create a ForEach loop and use it to extract this value, but it displays absolutely all the available values, and not the one I need. Tell me how to do it right? For display I use Spring MVC. Now the page looks like this:
I am also attaching the jsp content:
<form:form action="allSemestrsSem" method="get" id="selectSemestr" modelAttribute="semestr">
<tr>
<td style="font-size: large;">Select semester</td>
<td style="padding-left: 50px">
<select name="selectSemestr">
<c:forEach items="${semestr}" var="semestr">
<option value="${semestr.id}">${semestr.name}</option>
</c:forEach>
</select>
</td>
<td style="padding-left: 20px">
<security:authorize access="hasRole('ADMIN')">
<input type="submit" id="button" value="select" /></td>
</security:authorize>
</tr>
<tr height="80px" style="font-size: large; font-weight: bold;">
<td colspan="3">Semester duration:
<c:forEach items="${semestr}" var="semestr"> ${semestr.duration} </c:forEach>
</td>
</tr>
</form:form>
And just in case my controller is:
#RequestMapping(value = "/allSemestrsSem", method = RequestMethod.GET)
public String allSemestrs(#RequestParam(name = "selectSemestr") String selectSemestr, Model model) {
List<Semestr> semestr = service.getSemestr();
model.addAttribute("semestr", semestr);
List<Discipline> allDisciplines = service.getDisciplineSemestrId(Integer.valueOf(selectSemestr));
model.addAttribute("allDisc", allDisciplines);
return "semestr";
}

How to bind values to thymeleaf object from html table row and send to spring MVC controller's modelAttribute

I am new to Thymeleaf and tried all possible solutions and workaround from web. Desperately I need some help to figure out on what is wrong here.
Not able get cart object(thymeleaf) value updated from html to MVC controller. instead CartViewModel object's fields (userI and products) in cart and modelMap objects are coming as null. even it is not getting values which i passed to populate the table in previous call.
Any help is highly appreciated. Thanks for your valuable time in advance.
Controller debug status
Controller code:
#Controller
public class MyController {
#Autowired
IProductService productService;
#Autowired
IVendorService vendorService;
#RequestMapping(value = "/", method = {RequestMethod.GET})
public String getVendors(VendorViewModel vendorViewModel, final ModelMap modelMap) {
modelMap.addAttribute("vendors", vendorService.getVendors());
return "home";
}
#RequestMapping(value = "/inventory", method = {RequestMethod.POST})
public String getProductsForSelectedVendor(VendorViewModel vendorViewModel, final ModelMap modelMap) {
modelMap.addAttribute("vendors", vendorService.getVendors());
modelMap.addAttribute("cart", new CartViewModel("Nish", productService.getProductsByVendor(vendorViewModel.getId())));
return "home";
}
#RequestMapping(value = "/cart", method = {RequestMethod.POST})
public String saveCart(#ModelAttribute(name = "cart") CartViewModel cart, ModelMap modelMap, BindingResult bindingResult) {
**//not able get cart object value set in html via thymeleaf. **
return "cart";
}
}
#Data
#AllArgsConstructor
#NoArgsConstructor
#ToString
#JsonIgnoreProperties(ignoreUnknown = true)
public class CartViewModel {
private String userId;
//private List<ProductSelected> products;
private List<ProductViewModel> products;
}
#Data
#AllArgsConstructor
#NoArgsConstructor
#ToString
#JsonIgnoreProperties(ignoreUnknown = true)
public class ProductViewModel
{
public String id;
public int qty = 0;
public String handle;
public String title;
public String bodyHTML;
public String vendor;
public String type;
public String tags;
public String variantPrice;
public String imageSrc;
public String imageAltText;
}
<div class="container-main">
<form method="POST" enctype="multipart/form-data" th:object="${vendorViewModel}" th:action="#{/inventory}">
<div>
<div style="float:left;padding-left:10px;">
<label for="selectVendor" class="input-label-name">Select Vendor</label>
</div>
<div style="float:left;padding-left:10px;">
<select id="selectVendor" required="required" th:field="*{id}" style="width:90%">
<option value=""></option>
<option th:each="vendor, iSat : ${vendors}" th:value="${vendor.id}" th:with="test=${vendor.name}" th:text="${vendor.name}">
</option>
</select>
</div>
<div style="float:left;padding-left:10px;">
<input type="submit" value="Get Products" class="btn">
</div>
</div>
</form>
<form method="POST" action="#" role="form" enctype="multipart/form-data" th:action="#{/cart}" th:object="${cart}">
<!-- https://stackoverflow.com/questions/49462788/how-to-post-a-list-to-controller-in-thymeleaf-->
<div style="padding-top: 50px;">
<table id="productTable" class="tableBodyScroll">
<tr>
<th>Quantity</th>
<th>QtyPrice</th>
<th>Handle</th>
<th>Title</th>
<th>Type</th>
<th>Tags</th>
<th>Price</th>
<th>Image</th>
<!-- <th>IN STOCK</th>-->
</tr>
<tr th:id="${prod.id}" th:each="prod,iterStat : ${cart.products}" th:class="${iterStat.odd}? 'odd'">
<td><span class="table-add"><button th:id="'add_' + ${prod.id}" type="button" class="btn btn-rounded btn-sm my-0">+</button></span>
<span type="text" th:id="'count_' + ${prod.id}" th:field="*{products[__${iterStat.index}__].qty}">0</span>
<span class="table-remove"><button th:id="'remove_' + ${prod.id}" type="button" class="btn btn-danger btn-rounded btn-sm my-0">-</button></span>
</td>
<td><span th:id="'qtyPrice_' + ${prod.id}">0</span></td>
<td th:text="${prod.handle}"></td>
<td th:text="${prod.title}"></td>
<td th:text="${prod.type}"></td>
<td th:text="${prod.tags}"></td>
<td th:id="'price_' + ${prod.id}" th:text="${prod.variantPrice}"></td>
<td><img height="50px" width="100px" th:src="${prod.imageSrc}" th:title="${prod.title}" th:alt="${prod.imageAltText}" /></td>
<!-- <td th:text="${prod.inStock}? #{true} : #{false}">yes</td>-->
</tr>
</table>
</div>
<div style="float:left;padding-left:10px;">
<input type="submit" value="Go to Cart" class="btn">
</div>
</form>
</div>
I actually did some work around to overcome my problem using ajax call. But later I came across this, seems to be a better fit for my scenario.
Hope it would help someone.

Spring #ModelAttribute not working

I have a controller in which I put both the get and post methods. Both methods work fine but when I introduce #ModelAttribute annotation to the POST method, it starts giving me Status 400 -- The request sent by the client was syntactically incorrect.
I do not know what I am doing wrong. The View looks like:
<form:form method="post" action="createIdeaPublic" commandName="idea">
<table class="form">
<tr>
<td align="right"><label for="title">Idea Title</label></td><td align="left"><form:input type="text" path="title" id="title"></form:input></td>
<td align="right"><label for="email">Your Email</label></td><td align="left"><form:input type="text" path="requestorEmail" id="requestorEmail" class="emails"></form:input></td>
</tr>
<tr>
<td align="right"><label for="partnerEmail">CI Contact Email</label></td><td align="left"><form:input type="text" path="cIInitialContact" id="cIInitialContact" class="emails"></form:input></td>
<td align="right"><label for="sponsorEmail">Sponsor Email</label></td><td align="left"><form:input type="text" path="sponsorEmail" id="sponsorEmail" class="emails"></form:input></td>
</tr>
<tr>
<td align="right"><label for="requestedDeliveryDate">Requested Delivery Date</label></td><td align="left"><form:input type="text" path="requestedDeliveryDate" id="requestedDeliveryDate" class="datepicker"></form:input></td>
<td align="right"><label>Classification</label></td><td align="left">
<label for="discretionary" class="radio">Discretionary</label>
<form:radiobutton path="stateDescription" id="discretionary" value="Discretionary"></form:radiobutton>
<label for="mandatory" class="radio">Mandatory</label>
<form:radiobutton path="stateDescription" id="mandatory" value="Mandatory"></form:radiobutton>
<label for="regulatory" class="radio">Regulatory</label>
<form:radiobutton path="stateDescription" id="regulatory" value="Regulatory"></form:radiobutton>
</td>
</tr>
<tr>
<td colspan="4" align="right"><input type="submit" class="ui ui-button ui-corner-all ui-widget" style="margin-top: .6em; margin-right: 1em;font-weight: bold;font-size: 1.2em; width: 150px;" value="Create Idea" /></td>
</tr>
</table>
</form:form>
I tried changing the commandName="idea" to modelAttribute="idea" but no benifit.
The Spring controller looks like
#Controller
#RequestMapping ("/createIdeaPublic")
public class CreateIdeaPublicController{
#RequestMapping(method = RequestMethod.GET)
public ModelAndView view(ModelMap model) {
model.addAttribute("areas",Utils.areas);
return new ModelAndView("createIdeaPublic", "idea", new Idea());
}
#RequestMapping(method = RequestMethod.POST)
public String submit(#ModelAttribute("idea")Idea idea, ModelMap model) {
// System.out.println(idea.getTitle());
System.out.println("Hello World");
return "redirect:createIdeaPublic";
}
}
But as soon as I remove the #ModelAttribute("idea")Idea idea, from the submit method, the form submission starts working.
I figured it out. It was issue with the date field. If I dont enter a value into the date field, I would get 400 page. So here is what I did in the Controller class. Copy as it is if you have this issue (Just change the date format accordingly).
#InitBinder
public void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");
dateFormat.setLenient(false);
// true passed to CustomDateEditor constructor means convert empty String to null
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
}

InitBinder on multiple form objects

Can you have a #Initbinder on several data in the same form?
I have a spring form which contains a select dropdown of an object and two datafields, I have an Initbinder on the dates otherwise I get an error on submit. But I also need to bind the dropdown to an object.
I have a Type which has two dates and a Category, and it is the Category I need to bind because it can not be empty on save.
I think it will help me to validate the form to.
So can I have this in my Type controller?
#InitBinder
public void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
binder.registerCustomEditor(Category.class, "category", new CategoryEditor(CategoryService));
}
And this is the Editor:
public class CategoryEditor extends PropertyEditorSupport {
private CategoryService categoryService;
public CategoryEditor(CategoryService categoryService) {
this.categoryService = categoryService;
}
#Override
public void setAsText(String text) throws IllegalArgumentException {
if (text.equals("0")) {
this.setValue(null);
} else {
Category sc = categoryService.getCategory(Integer.parseInt(text));
this.setValue(sc);
}
}
#Override
public String getAsText() {
Category parent = new Category();
if (this.getValue() != null) {
parent = (Category) this.getValue();
}
return "";
}
}
And my jsp-page
<s:url value="/mvc/type/save" var="actionUrl" />
<sf:form method="POST" modelAttribute="type" action="${actionUrl}">
<fieldset>
<legend><s:message code="${heading}" /></legend>
<table>
<tr>
<th><label for="category"><s:message code="category" />:</label></th>
<td><sf:select path="category.ID" id="category">
<sf:option value="0"> </sf:option>
<sf:options items="${listOfCategories}" itemLabel="name" itemValue="ID" />
</sf:select></td>
</tr>
<tr>
<th><label for="name"><s:message code="name" />:</label></th>
<td><sf:input path="name" id="name" />
<sf:hidden path="ID" />
<sf:hidden path="version" /></td>
</tr>
<tr>
<th><label for="marketing"><s:message code="marketing" />:</label></th>
<td><sf:input path="marketingFunction" id="marketing" /></td>
</tr>
<tr>
<th><label for="status"><s:message code="status" />:</label></th>
<td><sf:select path="lifeCycleStatus">
<sf:option value="0"> </sf:option>
<sf:options items="${listOfEnums}" />
</sf:select></td>
</tr>
<tr>
<th><label for="validfrom"><s:message code="validfrom" />:</label></th>
<td><sf:input path="validFrom" id="validfrom" /></td>
</tr>
<tr>
<th><label for="validuntil"><s:message code="validuntil" />:</label></th>
<td><sf:input path="validUntil" d="validuntil" /></td>
</tr>
<tr>
<td colspan="2">
<input id="saveButton" class="right" type="submit" title="<s:message code="save" />" value=" [ <s:message code="save" /> ] " />
</td>
</tr>
</table>
</fieldset>
</sf:form>
So my question: Can I have multiple binder in the same initBinder in my controller? It seems like I can't because I never enter the CategoryEditor. How do I do this?
There aren't multiple binders, there are multiple PropertyEditors.
Your custom one is never called because you are binding the wrong path.
<sf:select path="category.ID" id="category">
You must bind to category and not category.ID
<sf:select path="category" id="category">

unable to submit form data using form:input

I'm using Spring 3 MVC(3.0.1 Release). I have a bean class Customer and a CustomerForm class as shown below.
public class Contact {
private String firstname;
private String lastname;
private String email;
private String phone;
//getters and setters
}
public class ContactForm {
private List<Contact> contacts;
//getters and setters
}
From Spring Controller I'm populating the contacts and setting it to Model as shown below
private static List<Contact> contacts = new ArrayList<Contact>();
static {
contacts.add(new Contact("Barack", "Obama", "barack.o#whitehouse.com", "147-852-965"));
contacts.add(new Contact("George", "Bush", "george.b#whitehouse.com", "785-985-652"));
contacts.add(new Contact("Bill", "Clinton", "bill.c#whitehouse.com", "236-587-412"));
contacts.add(new Contact("Ronald", "Reagan", "ronald.r#whitehouse.com", "369-852-452"));
}
#RequestMapping(value = "/get", method = RequestMethod.GET)
public ModelAndView get() {
ContactForm contactForm = new ContactForm();
contactForm.setContacts(contacts);
return new ModelAndView("add_contact" , "contactForm", contactForm);
}
In JSP I'm displaying it as editable form as below,
<form:form method="post" action="save.html" modelAttribute="contactForm">
<table>
<tr>
<th>No.</th>
<th>Name</th>
<th>Lastname</th>
<th>Email</th>
<th>Phone</th>
</tr>
<c:forEach items="${contactForm.contacts}" var="contact" varStatus="status">
<tr>
<td align="center">${status.count}</td>
<td><form:input path="contacts[${status.index}].firstname"/></td>
<td><form:input path="contacts[${status.index}].lastname"/></td>
<td><form:input path="contacts[${status.index}].email" /></td>
<td><form:input path="contacts[${status.index}].phone"/></td>
</tr>
</c:forEach>
</table>
<br/>
<input type="submit" value="Save" />
</form:form>
If i'm making modification to the contacts and submiting it the values are not set to the model. But instead of using Spring JSTL if i use plain HTML input tags as shown below the values are getting set to model.
<c:forEach items="${contactForm.contacts}" var="contact" varStatus="status">
<tr>
<td align="center">${status.count}</td>
<td><input name="contacts[${status.index}].firstname" value="${contact.firstname}"/></td>
<td><input name="contacts[${status.index}].lastname" value="${contact.lastname}"/></td>
<td><input name="contacts[${status.index}].email" value="${contact.email}"/></td>
<td><input name="contacts[${status.index}].phone" value="${contact.phone}"/></td>
</tr>
</c:forEach>
When I checked the rendered JSP's HTML source from the browser, I could find a difference in the input control name's as below
//JSP
<form:input path="contacts[${status.index}].firstname"/>
//corresponding HTML not working
<input id="contacts0.firstname" name="contacts0.firstname" type="text" value="Barack"/>
//JSP
<input name="contacts[${status.index}].firstname" value="${contact.firstname}"/>
//corresponding HTML working!
<input name="contacts[0].firstname" value="Barack"/>
Is this a known issue or is there something I'm missing?
Regards,
Makesh.
You can directly use name attribute in the form:input tag like below,it may work.
<td><form:input name="contacts[${status.index}].firstname" path="contacts[${status.index}].firstname"/></td>

Resources