Dynamic row thymeleaf - spring

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.

Related

Spring Boot doesnt return template in Post Mapping

I hope some of you have an idea about this. I'm still new to Spring but till this point I could figured out every problem myself. I'm using also Thymeleaf for the templates.
In the Controller the function for #GetMapping works as intended, but the #PostMapping function doesnt return the template which I specified in the return parameter and so the browser shows the wrong page.
The #PostMapping function gets triggered by the ajax statement in the html script.
I get no errors or warnings on server side as well as on client side. So I have no idea were the spot the mistake.
Hope some of you can spot the mistake. Thanks in advance anyway.
Controller:
#Controller
public class SpringController {
#RequestMapping(value="/tank", method = RequestMethod.GET)
public String loadData(Model model){
Tankstelle[] data = TankerAPI.getTankData(lat, lng, 5, sort, type);
model.addAllAttributes(convertdata("name", data));
model.addAllAttributes(convertdata("dist", data));
model.addAllAttributes(convertdata("price", data));
return "home";
}
#RequestMapping(value = "/tank", method = RequestMethod.POST)
public String getChangedData(#RequestBody JSONObject incomingjson) {
lat = Float.parseFloat(incomingjson.get("lat").toString());
lng = Float.parseFloat(incomingjson.get("lng").toString());
BigDecimal biglat = new BigDecimal(lat).setScale(3, RoundingMode.HALF_UP);
BigDecimal biglng = new BigDecimal(lng).setScale(3, RoundingMode.HALF_UP);
lat = biglat.floatValue();
lng = biglng.floatValue();
sort = "price"; //for seeing a difference
System.out.println(incomingjson.get("lat")+" "+incomingjson.get("lng"));
return "test"; //here it should return the template "test" but it return "home"
}
home.html:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org/">
<head>
<meta charset="UTF-8"/>
<title>guenstigertanken.de</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<style>...
</style>
</head>
<body onload="getLocation()">
<div class="ueberschriftbox"></div>
<h2 class="header">guenstigertanken.de</h2>
<h2 class="subheader">Wir finden die günstigsten Spritpreise für Sie</h2>
<div class="backrounding">
<div class="steuerelementebox"></div>
<div class="steuerelementespritsorteueberschrift">
<h2>Spritsorte</h2>
<label class="container1">Super E5
<input type="radio" id="E5" name="radio1" onclick="autoSubmit()">
<span class="checkmark1"></span>
</label>
<label class="container1">Super E10
<input type="radio" id="E10" name="radio1" onclick="autoSubmit()">
<span class="checkmark1"></span>
</label>
<label class="container1">Diesel
<input type="radio" id="Diesel" name="radio1" onclick="autoSubmit()">
<span class="checkmark1"></span>
</label>
</div>
<div class="steuerelementesortierennachueberschrift">
<h2>Sortieren nach</h2>
<label class="container2">Preis aufsteigend
<input type="radio" checked="checked" name="radio2" onclick="autoSubmit()">
<span class="checkmark2"></span>
</label>
<label class="container2">Preis absteigend
<input type="radio" name="radio2" onclick="autoSubmit()">
<span class="checkmark2"></span>
</label>
<label class="container2">Distanz aufsteigend
<input type="radio" name="radio2" onclick="autoSubmit()">
<span class="checkmark2"></span>
</label> <label class="container2">Distanz absteigend
<input type="radio" name="radio2" onclick="autoSubmit()">
<span class="checkmark2"></span>
</label>
<label class="container2" id="alph">Name alphabetisch
<input type="radio" name="radio2" onclick="autoSubmit()">
<span class="checkmark2"></span>
</label>
</div>
<div class="buttonsbox"></div>
<div class="tabellenbox"></div>
<div class="ueberuns">
<p>Über uns</p>
</div>
<div class="agb">
<p>AGB</p>
</div>
<div class="datenschutz">
<p>Datenschutz</p>
</div>
<div class="impressum">
<p>Impressum</p>
</div>
<div class="left show">
<table id="datatable">
<thead>
<tr> <th>Name</th> <th>Entfernung</th> <th>Preis</th> <th>Favorit</th> </tr>
</thead>
<tbody>
<tr> <td th:text="${name1}">AGIP</td> <td th:text="${dist1}">7km</td> <td th:text="${price1}">1,20€</td> <td><input type="checkbox"></td> </tr>
<tr> <td th:text="${name2}">Aral</td> <td th:text="${dist2}">12km</td> <td th:text="${price2}">1,23€</td> <td><input type="checkbox"></td> </tr>
<tr> <td th:text="${name3}">Esso</td> <td th:text="${dist3}">2km</td> <td th:text="${price3}">1,25€</td> <td><input type="checkbox"></td> </tr>
<tr> <td th:text="${name4}">Esso</td> <td th:text="${dist4}">10km</td> <td th:text="${price4}">1,25€</td> <td><input type="checkbox"></td> </tr>
<tr> <td th:text="${name5}">Esso</td> <td th:text="${dist5}">5km</td> <td th:text="${price5}">1,25€</td> <td><input type="checkbox"></td> </tr>
<tr> <td th:text="${name6}">BP</td> <td th:text="${dist6}">13km</td> <td th:text="${price6}">1,35€</td> <td><input type="checkbox"></td> </tr>
<tr> <td th:text="${name7}">BP</td> <td th:text="${dist7}">13km</td> <td th:text="${price7}">1,35€</td> <td><input type="checkbox"></td> </tr>
<tr> <td th:text="${name8}">BP</td> <td th:text="${dist8}">13km</td> <td th:text="${price8}">1,35€</td> <td><input type="checkbox"></td> </tr>
</tbody>
</table>
</div>
</div>
<a class="cssbuttonfavoriten" href="test.html">Favoriten></a>
<a class="cssbuttonleaderboard" href="test.html">Leaderboard></a>
<a class="cssbuttonstatistiken" href="test.html">Statistiken></a>
<script>
function getLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(showPosition);
} else {
}
}
function showPosition(position) {
var posdata = '{"lat":"'+position.coords.latitude+'","lng":"'+position.coords.longitude+'","typeselect":"'+type+'"}';
$.ajax({
type: 'post',
url: '/tank',
data: JSON.stringify(posdata),
contentType: "application/json; charset=utf-8",
traditional: true,
success: function(posdata) {
//alert("ajax success: refresh page to sort after price[prob current setting]")
},
error: function(){
alert("ajax error")
}
});
}
</script>
</body>
</html>
When you make any request through ajax you just receive corresponding response in your callback function:
success: function(posdata) {
//alert("ajax success: refresh page to sort after price[prob current setting]")
},
So browser will not show you a new template page. Your page is in "postdata" response

why i am not getting a list of item present in my cart into the url

I want to send a list of items for order but none of item "id" is present in url.
Cart.jsp
<c:forEach items="${listCart}" var="items">
<tr>
<td style="text-align: center">${items.id}</td>
<td style="text-align: center">${items.name}</td>
<td style="text-align: center"><fmt:formatNumber type="currency">${items.price}
</fmt:formatNumber></td>
<td>
<input type="number" style="width:20 px; height:4px" id="quantity_${items.id}"
value="${items.quantity}" />
<td style="text-align: center"><fmt:formatNumber
type="currency">${items.price.longValue() * items.quantity}</fmt:formatNumber></td>
<td style="text-align: center"><a href="<c:url value="/view/cart/removeItem"/>?
id=${items.id}" >Remove</a></td>
</c:forEach>
</table>
<div class="col-md-5">
<a class="btn btn-light btn-xl" href="<c:url value="/order"/>${items.id}">Order Now!</a>
</div>
This is controller class where the items will stored to database and will place for order.
Controller Class
#ResponseBody
#RequestMapping("/order/{id}")
public String createOrder(#PathVariable(value = "id") int id) {
customerOrderModel customerOrder = new customerOrderModel();
Cart cart = cartdao.getCartByID(id);
customerOrder.setCart(cart);
CustomerModel customer = cart.getCustomer();
customerOrder.setCustomerID(customer);
customerOrder.setBillingAddress(customer.getBillingAddress());
customerOrder.setShippingAddress(customer.getShippingAddress());
orderDao.addCustomerOrder(customerOrder);
return "redirect:/view/cart/addItem";
}
I think, you have pasted this code in wrong place, it should be with in foreach loop, because the "items" scope with in foreach. just put this code with for lop
<div class="col-md-5">
<a class="btn btn-light btn-xl" href="<c:url value="/order"/>${items.id}">Order Now!</a>
</div>

Thymeleaf binding a selected value from a table

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

Getting the selected values from a checkbox list to the controller with Spring Boot

I'm using Spring Boot with Thymeleaf as viewer, and I want to delete all selected items from a table. Therefore, I need to pass to the controller a list with the values from the selected checkboxes.
This is my approach for the controller:
#PostMapping("/admin/rates/prices/delete")
public String delete(#ModelAttribute Rate price, ServletWebRequest request){
if(request.getParameterValues("idChecked") != null){
for(String idCheckedStr : request.getParameterValues("idChecked")){
int idrate = Integer.getInteger(idCheckedStr);
rateRepository.deleteRate(idrate);
}
}
return "redirect:/admin/rates/prices";
}
I get a Null Pointer Exception:
java.lang.NullPointerException: null
at com.rentalwebs.controllers.rates.PriceListController.delete(PriceListController.java:42)
I think this method should collet the values::
request.getParameterValues("idChecked")
This is the line at the form, which creates each checkbox with the Thymeleaf annotations:
<input type="checkbox" th:name="idChecked" th:value="${price.idrate}"/>
That's the view code for the form:
<form th:action='#{/admin/rates/prices/delete}'
method="POST"
th:object="${rate}">
<button type="submit" name='delete' value="delete"
class='btn btn-secondary btn-sm'
th:text="#{delete}"
data-toggle="tooltip" data-placement="right"
th:title="#{delete.selected}">
</button>
<p></p>
<table class='table table-sm responsive'>
<thead class='thead-default'>
<tr>
<th><input type="checkbox" id="checkAll"/></th>
<th th:text='#{from}'></th>
<th th:text='#{to}'></th>
<th th:text='#{price}'></th>
</tr>
</thead>
<tbody>
<tr th:each="price : ${prices}">
<td>
<input type="checkbox" th:name="idChecked" th:value="${price.idrate}"/>
</td>
<td th:text="${#temporals.format(price.datefrom, 'dd/MM/yyyy')}"></td>
<td th:text="${#temporals.format(price.dateto, 'dd/MM/yyyy')}"></td>
<td th:text="${price.price} + ' €'"></td>
</tr>
</tbody>
</table>
</form>
Thank you in advance for your help :-)
This is the code with the solution, taken from the comments:
#PostMapping("/admin/rates/prices")
public String delete(#RequestParam("idChecked") List<String> idrates){
if(idrates != null){
for(String idrateStr : idrates){
int idrate = Integer.parseInt(idrateStr);
rateRepository.deleteRate(idrate);
}
}
return "redirect:/admin/rates/prices";
}

Values for th:field attributes in checkbox

I have table with datas from database (insert dynamically). In one column I insert checkbox. Now I want to select one of them and send to next form (I select one product and send properties to another form. In this form should be displayed properties only the select product). But I don't know what kind of value insert in th:field="*{}". I tried many solutions but doesn't work. My html form with all products table:
<form action="/oferta/zamow" th:action="#{/oferta/zamow}"
th:object="${oferta}" method="post">
<table border="1" id="display-data">
<tr>
<td>#</td>
<td>title</td>
<td>author</td>
<td>rok</td>
<td>cena</td>
<td></td>
</tr>
<tr th:each="produkt, pozycja : ${oferta}">
<td th:text="${pozycja.count}"></td>
<td><span th:text="${produkt.tytul}"></span></td>
<td><span th:text="${produkt.autor}"></span></td>
<td><span th:text="${produkt.rok}"></span></td>
<td><span th:text="${produkt.cena}"></span></td>
<td>
<input type="submit" value="zamow"/>
<!-- <a th:href="#{/zamowienie}">zamow</a> -->
</td>
<td>
<label>zamow</label>
<input type="checkbox" th:field="*{produkt}" th:value="${produkt}"/>
</td>
</tr>
</table>
</form>
Form to display select product:
<form action="/zamowienie/zam" th:action="#{/zamowienie/zam}"
th:object="${zamowienie}" method="post">
<table border="1" id="display-data">
<tr align="center">
<td colspan="2">twoje zamowienie</td>
</tr>
<tr>
<td>tytul</td>
<td><span th:text="${produkt.tytul}"></span></td>
</tr>
<tr>
<td>autor</td>
<td><span th:text="${produkt.autor}"></span></td>
</tr>
<tr>
<td>rok</td>
<td><span th:text="${produkt.rok}"></span></td>
</tr>
<tr>
<td>cena</td>
<td><span th:text="${produkt.cena}"></span></td>
</tr>
<tr>
<td>data zlozenia zamowienia</td>
<td><span th:text="${datazam}"></span></td>
</tr>
</table>
</form>
Thanks for help.
I am not sure if this is the answer you seek, but you can find an example at http://www.thymeleaf.org/doc/html/Thymeleaf-Spring3.html#checkbox-fields.
Here is a simple example to illustrate how to use a checkbox in Thymeleaf with Spring MVC.
Controller:
#RequestMapping(value = "/showForm", method=RequestMethod.GET)
public String showForm(Model model) {
List<String> allItems = new ArrayList<String>();
allItems.add("value1");
allItems.add("value2");
allItems.add("value3");
model.addAttribute("allItems", allItems);
Foo foo = new Foo();
List<String> checkedItems = new ArrayList<String>();
// value1 will be checked by default.
checkedItems.add("value1");
foo.setCheckedItems(checkedItems);
model.addAttribute("foo", foo);
...
}
#RequestMapping(value = "/processForm", method=RequestMethod.POST)
public String processForm(#ModelAttribute(value="foo") Foo foo) {
// Get value of checked item.
List<String> checkedItems = foo.getCheckedItems();
...
}
html:
<form action="#" th:action="#{/processForm}" th:object="${foo}" method="post">
<div th:each="item : ${allItems}">
<input type="checkbox" th:field="*{checkedItems}" th:value="${item}" />
<label th:text="${item}">example</label>
</div>
<input type="submit" />
</form>
Foo.java:
public class Foo {
private List<String> checkedItems;
public List<String> getCheckedItems() {
return checkedItems;
}
public void setCheckedItems(List<String> checkedItems) {
this.checkedItems = checkedItems;
}
}
Hope this helps.
Take a look at the thymeleaf spring integration docs.
All th:field are mapped against the command object. Thats why you need the *{} expression.
One thing the template engine is not able to do (yet) is mapping fields inside a loop directly. So you cannot use the *{} approach to reference the produkt variable from the loop.
What you have to do is use the index of the th:each expression and build a property accessor with a pre-evaluated expression for the index.
<input type="checkbox" th:field="*{produkts[__${index}__].checked" />
You do not need the th:value, th:field is taking care of it. (Except if you want to superseed it)

Resources