Thymeleaf - set attributes for objects in forms with hidden inputs - spring-boot

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.

Related

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

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

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
}

Jsrender template form validation

I have a form which is a jsrender template
<script id="editTemplate" type="text/x-jsrender">
<div>
<form id="myEditForm" method="POST" action="">
<table>
<tr>
<td>
Id:
</td>
<td>
<input type="text" name="id" value="{{>id}}" class="required" />
</td>
</tr>
<tr>
<td>
Name:
</td>
<td>
<input type="text" name="name" value="{{>name}}" class="required" />
</td>
</tr>
<td>
<input type="submit" value="submit" />
</td>
</tr>
</form>
I tried to add bassistance plugin based validation to the input fields but as the form is a jsrender template it is not working. any idea how to implement it???
The problem was i was adding the validation logic in the section of the html page where i created the form.
Validations could only be applied to the form once the template was compiled which happened in a seperate js file so, i added the validation code after the jsrender template was compiled ie., after these line in the js file
var html = $.render["edit-tmpl"](rowData); // adding row data to edit-tmpl
var edtdlg = $("#editDialog").html(html); // adding the template to
// editDialog
and now it works like charm:)

Resources