Write handler methods for dynamically generated buttons and text boxes - spring

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";
}

Related

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.

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

Pass id to the controller

Firstly, I am passing values from database to the table. In the first column I want to create a form that will pass the id to delete function in Controller.
<tr th:each="blg: ${all}" th:object="${blg}" >
<td>
<form th:action="#{/delete}" th:object= "${blg}" method="post">
<input type="text" th:field="${blg.id}"/>
<button type="submit">Delete</button>
</form>
</td>
<td th:text="*{title}"> title </td>
<td th:text="*{content}"> title </td>
<td th:text="*{category}"> title </td>
<td th:text="*{signature}"> title </td>
</tr>
Controller:
#GetMapping("/show")
public String show(Model model){
List<Blog> all = br.findAll();
model.addAttribute("all",all);
return "show";
}
#RequestMapping(value="/delete", method=RequestMethod.POST)
public String deletePost(#RequestParam Long id){
br.delete(id);
return "redirect:/show";
}
The thymeleaf engine doesn't map the object as this error occcurs:
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'blg' available as request attribute.
What is the way to create a correct form in this case?
Update your html code as shown below:
<tr th:each="blg: ${all}" >
<td>
<form th:action="#{|/delete/${blg.id}|}" method="post">
<button type="submit">Delete</button>
</form>
</td>
<td th:text="${blg.title}"> title </td>
<td th:text="${blg.content}"> title </td>
<td th:text="${blg.category}"> title </td>
<td th:text="${blg.signature}"> title </td>
</tr>
Better to use HTTP method DELETE for delete operations.
The reason because th:object tried to look in your request attribute for the attribute blg. But blg is a result of an iteration.

How to know from which jsp page the request has come to the controller in Spring MVC

I am developing an app where LoginForm.jsp and UserRegistration.jsp will lead to UserAccount jsp page on specific action.In LoginForm when a user presses a 'Login' button ->UserAccount form is displayed.In UserRegistration Form when details are entered and submitted ->UserAccount form is displayed.
Below is the controller code when a request comes as UserAccount.html
#RequestMapping(value="/UserAccount.html", method = RequestMethod.POST)
public ModelAndView userAccountForm(#Valid #ModelAttribute("user") UserDetails user,BindingResult result) {
if(result.hasErrors())
{ System.out.println(result.getAllErrors());
ModelAndView model1=new ModelAndView("UserRegistration");
return model1;
}
// User validation
System.out.println(user.getAccountType());
userDAO.create(user);
ModelAndView model1 = new ModelAndView("UserAccount");
return model1;
}
LoginForm.jsp
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<html>
<body>
<h1
style="background-color: green; color: Yellow; padding: 10px; text-align: center;">Mybank
Online Login</h1>
<form:errors path="user.*" />
<form action="/MyBankProject/UserAccount.html" method="post">
<div
style="background-color: yellow; color: black; padding: 10px; text-align: center;"
align="center">
<p>
User ID <input type="text" name="userName" />
</p>
<p>
Password <input type="password" name="password" />
</p>
<input type="submit" value="Login" /> New User?
</div>
<input type="hidden" name="page" value="LoginForm"/>
</form>
</body>
</html>
UserRegistration.jsp
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<html>
<body>
<h1>${headerMessage}</h1>
<h3>USER REGISTRATION</h3>
<form:errors path="user.*" />
<form action="/MyBankProject/UserAccount.html" method="post">
<table border="1" style="width:100%" >
<tr>
<td>FirstName :</td>
<td><input type="text" name="firstName" /></td>
</tr>
<tr>
<td>LastName :</td>
<td><input type="text" name="lastName" /></td>
</tr>
<tr>
<td>User's EmailId :</td>
<td><input type="text" name="emailId" /></td>
</tr>
<tr>
<td>user's gender :</td>
<td><select name="gender" multiple>
<option value="M">Male</option>
<option value="F">Female</option>
</select></td>
</tr>
<tr>
<td>user's age :</td>
<td><input type="text" name="age" /></td>
</tr>
<tr>
<td>user's DOB :</td>
<td><input type="text" name="dOB" /></td>
</tr>
<tr>
<td>Account Type :</td>
<td><select name="accountType" multiple>
<option value="Savings">Savings</option>
<option value="Current">Current</option>
<option value="FixedDeposit">Fixed Deposit</option>
</select>
<td>
</tr>
<tr>
<td>Amount :</td>
<td><input type="text" name="amount" /></td>
</tr>
</table>
<table>
<tr>
<td>UserName</td>
<td><input type="text" name="userName" /></td>
</tr>
<tr>
<td>Password</td>
<td><input type="password" name="password" /></td>
</tr>
</table>
<table>
<tr>
<td>User's Address :</td>
</tr>
<tr>
<td>country: <input type="text" name="address.country" /></td>
<td>city: <input type="text" name="address.city" /></td>
<td>street: <input type="text" name="address.street" /></td>
<td>pincode:<input type="text" name="address.pincode" /></td>
</tr>
<tr>
<td><button type="submit">Submit</button></td>
<td><input type="button" onclick="history.back();"
value="Cancel"></td>
</tr>
</table>
<input type="hidden" name="page" value="UserRegistration"/>
</form>
</body>
</html>
Now for me I need to know the jsp page from which the UserAccount invoked ,to perform different actions based on that.
I am new to Spring MVC and searched all over the internet for the solution.
HTTP is a stateless protocol. This means that you can not make assumptions about previous states.
I think you have two options to get information about the previous page anyways.
Add a hidden field to your form and send the name of the page with the request.
HTML:
<form>
<input type="hidden" name="page" value="[the name of the current page]"/>
</form>
Controller:
#RequestMapping(value="/UserAccount.html", method = RequestMethod.POST)
public ModelAndView userAccountForm(#RequestParam(value="page", required=false) String page) {
[...]
}
Now you can check the page value.
Use the session. Store the current page in the controller methods that render the form:
#RequestMapping("/login")
public String login(HttpServletRequest request) {
request.getSession().setAttribute("lastPage", "login");
return "redirect:/UserAccount.html";
}
#RequestMapping("/register")
public String register(HttpServletRequest request) {
request.getSession().setAttribute("lastPage", "register");
return "redirect:/UserAccount.html";
}
Check the lastPage value in userAccountForm():
#RequestMapping(value="/UserAccount.html", method = RequestMethod.POST)
public ModelAndView userAccountForm(HttpServletRequest request) {
HttpSession session = request.getSession();
String lastPage = (String) session.getAttribute("lastPage");
session.removeAttribute("lastPage");
[...]
}
Check the referer field of the request header.
#RequestMapping(value="/UserAccount.html", method = RequestMethod.POST)
public ModelAndView userAccountForm(#RequestHeader(value = "referer", required = false) String referer) {
[...]
}
This gives you the referer as a method argument if there is one. Be careful. It is possible that there was no referer or that the field was manipulated by the client.
Hope this will solve your need.
URL url = new URL(request.getHeader("referer"));
System.out.println("last page url"+ url.getPath());
if(url.getPath().equals("your last url"){
//code
}

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