Thymeleaf binding a selected value from a table - spring

I'm having a table with multiple elements that are generated from a List and every table element from that list has a button.By clicking that button there is a post submit request which should bind the values from that table data element to a #ModelAttribute object in spring boot.
The problem is that i'm able to map the whole list but I want to bind only the table element where the button was pressed.
<div class="table-responsive">
<form th:action="#{/saveAd}" th:object="${object}" method="POST">
<table class="table table-sm table-hover">
<thead class="thead-dark">
<tr>
<th>Image</th>
<th>Title</th>
<!-- <th style="width: 16.66%">Link</th> -->
<th>Price</th>
<th>City</th>
</tr>
</thead>
<tbody id="myTable">
<th:block th:each="element, itemStat: *{lista}">
<tr
th:onclick="'javascript:rowClicked(\'' + *{lista[__${itemStat.index}__].url} + '\');'">
<input type="hidden" readonly="readonly"
th:name="?"
th:value="${element.getUrl()}" />
<td>
<input type="hidden" readonly="readonly" th:name="img" th:value="${element.getImg()}" />
<img th:src="*{lista[__${itemStat.index}__].img}" class="size" name="img" />
</td>
<td>
<input type="hidden" readonly="readonly" th:name="?" th:value="${element.getTitle()}" />
<span th:text="*{lista[__${itemStat.index}__].title}"></span>
</td>
<td>
<input type="hidden" readonly="readonly" th:name="?" th:value="${element.getPrice()}" />
<span th:text="*{lista[__${itemStat.index}__].price}"></span>
</td>
<td>
<input type="hidden" readonly="readonly" th:name="?" th:value="${element.getCity()}" />
<span th:text="*{lista[__${itemStat.index}__].city}"></span>
</td>
<td><input class="btn btn-danger" type="submit"
value="Save"></td>
</tr>
</th:block>
</tbody>
</table>
</form>
</div>
The Controller:
#RequestMapping(path = "/saveAd", method = RequestMethod.POST)
public String saveAd(#ModelAttribute("Value") ListCreationAutovitDto listCreationAutovitDto) {
return "home";
}
I have a hidden input type for every td which should map the values but i've tried naming it in different ways but i can't make it work.Are there any ways to bind only the values where the button was pressed?

Idea 1: Make each td contain a form pointing to your controller method. Then bind the values you want to pass to the model by using hidden input fields in the form. Each button would then do a submit for the form it is in.
Idea 2: If you're mapping json to your java objects in your app, you can construct the json request body in JavaScript to contain only the stuff you want for that request

Related

Form is empty when submitted using Thymeleaf

I want to fill a list of applications for a university in the frontend. Each entry is supposed to hold two buttons: one for accepting the application and one for rejecting it. I created one form for each submit-button each.
<div class="container">
<div class="row">
<div class="col-12">
<table class="table table-hover">
<thead>
<tr>
<th scope="col"></th>
<th scope="col">Firstname</th>
<th scope="col">Lastname</th>
<th scope="col">Grade</th>
<th scope="col">NC</th>
<th scope="col">Course</th>
<th scope="col">Certificate</th>
<th scope="col">Recommendation</th>
<th scope="col">Decision</th>
</tr>
</thead>
<tbody>
<tr th:each="applicationOpen, rowStat: ${lstOpApplications}">
<th th:text="${rowStat.count}">1</th>
<td th:text="${applicationOpen.firstname}">firstname</td>
<td th:text="${applicationOpen.lastname}">lastname</td>
<td th:text="${applicationOpen.highschool_grade}">grade</td>
<td th:text="${applicationOpen.nc}">nc</td>
<td th:text="${applicationOpen.name}">coursename</td>
<td th:text="${applicationOpen.highschool_certificate}">certificate</td>
<td th:text="${applicationOpen.document}">recommendation</td>
<td>
<form action="#" th:action="#{/Bewerberubersicht}" th:object="${decisionForm}" method="post">
<input type="hidden" name="application_id" th:field="*{application_id}" value=${applicationOpen.id}"/>
<input type="hidden" name="decision" th:field="*{decision}" value=1/>
<button type="submit" class="btn btn-success"><i class="fas fa-edit"></i>Accept</button>
</form>
<form action="#" th:action="#{/Bewerberubersicht}" th:object="${decisionForm}" method="post">
<input type="hidden" name="application_id" th:field="*{application_id}" value=${applicationOpen.id}/>
<input type="hidden" name="decision" th:field="*{decision}" value=2/>
<button type="submit" class="btn btn-danger"><i class="fas fa-edit"></i>Reject</button>
</form>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
When I look in the Browser's Dev-Tools I can see that the content of the POST-request is
application_id: ""
decision: ""
When I replace value=${applicationOpen.id} with e.g. value=5 it is still empty. Hence, it should not be a problem with applicantOpen. Also, the list in the frontend is being filled just fine, so all of that should work. I first thought is is a problem with the DecisionForm class, but it seems my subsequent problems are caused by the issue described here.
The th:field attribute overrides content of name and value attributes. The decisionForm object seems to have empty fields so all forms have empty values.
Basically using th:object with th:field is convenient when you need to have your form prepopulated with values. It establishes initial state of your form, not the target. Using them makes sense for single form, not for multiple forms in loop with varying values.
In your case: please remove both th:object and th:field attributes. Instead use value="1" or value="2" for decision input, and th:value="${applicationOpen.id}" for application_id input.

Dynamic row thymeleaf

I want to create dynamic adding and removing list rows in Thymeleaf and Spring Boot.
I don't know how to use thymeleaf in dynamic rows, but i am trying to do it
So this is my code:
public class Form{
private List<Obj> list = new ArrayList<>();
//...
}
public class Obj{
private String a;
}
Controller:
#GetMapping("/form")
public String form(Model model) {
model.addAttribute("form", new Form());
return "/form";
}
HTML:
[...]
<form class="form-horizontal row-border" action="#" th:action="#{/form}" th:object="${form}" method="post">
<div class="form-group">
<div class="col-md-12">
<div class="row">
<label class="col-md-2 control-label">...</label>
<div class="col-md-10">
<table id="myTable" class=" table order-list">
<thead>
<tr>
<td>String</td>
</tr>
</thead>
<tbody>
<tr th:each="row:${list}">
<td class="col-sm-1">
1
</td>
<td class="col-sm-3">
<input th:field="*{list[__${row.index}__].a}" type="text" name="xyz" class="form-control"/>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="5" style="text-align: left;">
<input type="button" class="btn btn-lg btn-block " id="addrow" value="Add row" />
</td>
</tr>
<tr>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
</div>
</form>
[...]
but i don't know how to get send data from form to controller
The controller recieves your data based on the input names. th:field sets your input name in the proper way so the controller will recieve it.
Sadly, if you add a new row in the client, where thymeleaf doesn't exist anymore, you have to set the proper name manually. You can look up for which name to set in your new row inputs with a js function like this (or any other way):
var nextRow = 0;
while($("input[name='list[" + nextRow + "].a']").length){
nextRow ++;
}
nextRow will have the next free row, just use it to build a name in the fashion of the ones thymeleaf generates and set it as your new input's name.

Thymeleaf - set attributes for objects in forms with hidden inputs

I got this peace of html:
<form action="#" th:action="#{${cartServiceBaseUrl}+'/addCatalogItemToCart'}" th:object="${cartCatalogItem}" method="post">
<input type="hidden" th:value="${cartId}" th:field="*{cartId}" />
<input type="hidden" th:value="${catalogItem.catalogItemId}" th:field="*{catalogItemId}" />
<input type="submit" value="add to cart" />
</form>
Unfortunately both values are null and I dont really understand why, because both values, cartId and catalogItem.catalogItemId are available on the page.
How can I properly pass them?
Java endpoint
#PostMapping(ADD_CATALOG_ITEM_TO_CART)
public void addCatalogItemToCart(#ModelAttribute CartCatalogItem cartCatalogItem, HttpServletResponse response) {
Full HTML:
<h1 th:text="${catalog.name}">...</h1>
<a th:href="#{${customerServiceBaseUrl}+'/home/'+__${cartId}__}">home</a>
<p th:text="'Catalog ID:'+${catalog.catalogId}"></p>
<p th:text="'Name: '+${catalog.name}"></p>
<table>
<th:block th:each="catalogItem : ${catalog.catalogItems}">
<tr>
<th>article id</th>
<th>catalogitem id</th>
<th>name</th>
<th>price</th>
</tr>
<tr>
<td th:text="${catalogItem.articleId}">...</td>
<td th:text="${catalogItem.catalogItemId}">...</td>
<td th:text="${catalogItem.name}">...</td>
<td th:text="${catalogItem.price}">...</td>
<td><form action="#" th:action="#{${cartServiceBaseUrl}+'/addCatalogItemToCart'}" th:object="${cartCatalogItem}" method="post">
<input type="hidden" th:value="${cartId}" th:field="*{cartId}" />
<input type="hidden" th:value="${catalogItem.catalogItemId}" th:field="*{catalogItemId}" />
<input type="submit" value="add to cart" />
</form></td>
</tr>
</th:block>
</table>
My goal: assign attributes to objects with hidden input fields and pass them for the controller methods.
You are not set your values from controller that's why there are both values null.
Use this code on your controller.
For example:
Model and View model=new Model and View ("Your thyme leaf template name");
model.put('your key name by which you an access on your thyme leaf
page',cart Id);
return model;
This will work.

Spring MVC4 + Thymeleaf Form data to Controller

I am working on a Spring MVC4 application that uses Thymeleaf as a templating engine. I have a table with team member information that has an embedded form in each row with a delete button to allow deleting a user by row. below is the table section and a screenshot of what it currently looks like...
<table class="bordered">
<thead>
<tr>
<th data-field="id">Name</th>
<th data-field="name">Password</th>
<th data-field="price">Email</th>
</tr>
</thead>
<tbody>
<tr th:each="user : ${users}">
<td th:text="${user.userName}">Name</td>
<td th:text="${user.password}">Password</td>
<td th:text="${user.email}">Email</td>
<td>
<!-- Submit the user to be deleted -->
<form th:object="${user}" class="col s12" action="/deleteUser" method="post">
<button class="btn waves-effect waves-light red col" type="submit" name="deleteUser">
Delete
</button>
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
</form>
</td>
</tr>
</tbody>
</table>
When I click the delete button my TeamController picks up the request but all the instance variables are null, I'm fairly new to Spring MVC4 and Thymeleaf so not sure if this is a valid approach or not? I am assuming that the object in the form is a reference to the user object in the row so not sure why its coming null on the controller side?
Note: the user object I am passing in the form is an instance of UserAccount.java that lives in the Model.
Was able to fix it with below updates to my form...
<td>
<!-- Submit the user to be deleted -->
<form th:object="${UserAccount}" class="col s12" th:action="#{/deleteUser}" method="post">
<button class="btn waves-effect waves-light red col" type="submit" name="deleteUser">Delete</button>
<input type="hidden" id="userName" name="userName" th:value="${user.userName}" />
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
</form>

Write handler methods for dynamically generated buttons and text boxes

I have this small data on JSP form. I use JSTL to iterate over data. In each row, I have two dynamically generated textboxes and a button. Question is: how do I write a generic handler method for this data, which handles dynamically generated textboxes and buttons. Here is my JSP
<c:forEach items="${menus}" var="menu" >
<tr>
<td align="left" >${menu.getMenuId()}</td>
<td align="right"><input type="text" name="menu_name" value="${menu.getMenuName()}"/></td>
<td align="right"><input type="text" name="menu_price" value="${menu.getMenuPrice()}"/></td>
<td align="right"><c:out value="${menu.getIsAvailable()}" /></td>
<td align="right"><input type="submit" value="Add Item ${menu.getMenuId()}"></td>
</tr>
</c:forEach>
<h4>Add Product</h4>
Name: <input type="text" name="chosen_menu_name" />
Price: <input type="text" name="chosen_menu_price" />
<input type="submit" value="Add to cart">
And here is my controller (though I dont know what to put it there - at the moment I am using two seperate textbox and a button for taking the input)
#RequestMapping(method = RequestMethod.POST)
public ModelAndView AddMenu(#RequestParam("chosen_menu_name") String mymenu, #RequestParam("chosen_menu_price") String menu_price, #ModelAttribute("cart") ArrayList<Menu> mycart, Model model)
{
Menu menu = new Menu();
menu.setMenuId(0);
menu.setMenuName(mymenu);
menu.setMenuPrice(Double.parseDouble(menu_price));
model.addAttribute("menus", GetMenus());
mycart.add(menu);
return new ModelAndView("edit_menu");
//return "show_menu";
}
As one can see from the JSP, I am using two seperate textboxes and a button for taking input and passing it to the controller. How do I write a generic handler method for this data, which handles dynamically generated textboxes and buttons?
I'm assuming that you don't want those other two field and button. You want to add items directly from table.
You have to make each and every row as one form like below :
<c:forEach items="${menus}" var="menu" >
<tr>
<form method="POST" action="controllerName">
<td align="left">
${menu.getMenuId()}
<input type="hidden" name="menu_id" value="${menu.getMenuId()}"/>
</td>
<td align="right">
<input type="text" name="menu_name" value="${menu.getMenuName()}"/>
</td>
<td align="right">
<input type="text" name="menu_price" value="${menu.getMenuPrice()}"/>
</td>
<td align="right">
<c:out value="${menu.getIsAvailable()}"/>
</td>
<td align="right">
<input type="submit" value="Add Item">
</td>
</form>
</tr>
</c:forEach>
And controller like below:
#RequestMapping(method = RequestMethod.POST)
public ModelAndView AddMenu(#RequestParam("menu_id") String menuId, #RequestParam("menu_name") String mymenu, #RequestParam("menu_price") String menu_price, #ModelAttribute("cart") ArrayList<Menu> mycart, Model model)
{
Menu menu = new Menu();
menu.setMenuId(menuId);
menu.setMenuName(mymenu);
menu.setMenuPrice(Double.parseDouble(menu_price));
model.addAttribute("menus", GetMenus());
mycart.add(menu);
return new ModelAndView("edit_menu");
//return "show_menu";
}

Resources