How to select one object from the list of objects - java

Html
<form th:object="${klient}" th:action="#{/osoba}" method="post">
<div class="form-row">
<div class="form-group col-md-4">
<label >Imię</label>
<input type="text" class="form-control" th:field="*{imie}" >
</div>
<div class="form-group col-md-4">
<label >Nazwisko</label>
<input type="text" class="form-control" th:field="*{nazwisko}" >
</div>
<div class="select-list" id="selectlist">
<select th:field="*{UserId}" >
<option> -- </option>
<option th:each=" users : ${user}"
th:value="${users.UserId}"
th:utext="${users.lastName}"/>
</select>
</div>
Cod
#RequestMapping (value = "/osoba", method = RequestMethod.POST)
public String dodaj (Klient klient){
System.out.print(klient);
return "redirect:/osoba";
}
#RequestMapping (value = "/dodaj" , method = RequestMethod.GET)
public String tworz (Model model){
model.addAttribute("klient" , new Klient());
List<User> lista = userService.getAllUser();
model.addAttribute("user" , lista);
return "dodaj";
}
I want to create a form in which he completes the fields for the client and assign an existing user to him.The problem is that I can't get the selected user id.
I get a message about the first select
Error during execution of processor 'org.thymeleaf.spring5.processor.SpringSelectFieldTagProcessor

The error message shows that there is a problem with a property named uzytkownik , which I don't see anywhere in your template. Of course, you didn't include the complete template, so all I can say is that the problem is coming from somewhere else... apparently from line 178.

Create the getter and setter methods for userId in Klient:
public String getUserId()
public void setUserId(String userId)

I found a solution:
in select I changed
<select id="UserId" name="UserId" >
in controller
#RequestMapping (value = "/osoba", method = RequestMethod.POST)
public String dodaj (#ModelAttribute("UserId") Set<User> user, Klient klient){
klient.setUsers(user);
System.out.print(klient);
klientServicee.createOrUpdateKlient(klient);
return "redirect:/osoba";
}
it works but is it correct?

Related

Creating ArrayList from thymelaf form input values

I have a form for adding products written in HTML and thymeleaf.
<form th:action="#{/products/get}" th:object="${form}" method="post">
<div id="fields">
<label for="name"></label><input type="text" id="name" name="name" autofocus="autofocus" placeholder="NAME" required/><br>
<label for="label"></label><input type="text" id="label" name="label" autofocus="autofocus" placeholder="LABEL" required/><br>
Below the form, there is a button that adds two input fields to the form every time it's pressed. New input fields are the same as those two input fields above. The idea is that the user can enter data for as many products as he wants using the same form. For example, after pressing the button once the form will look like that:
<form th:action="#{/products/get}" th:object="${form}" method="post">
<div id="fields">
<label for="name"></label><input type="text" id="name" name="name" autofocus="autofocus" placeholder="NAME" required/><br>
<label for="label"></label><input type="text" id="label" name="label" autofocus="autofocus" placeholder="LABEL" required/><br>
<label for="name"></label><input type="text" id="name" name="name" autofocus="autofocus" placeholder="NAME" required/><br>
<label for="label"></label><input type="text" id="label" name="label" autofocus="autofocus" placeholder="LABEL" required/><br>
The thing is I'd like to create ArrayList of class ProductForm using the values from input fields and then pass it to my controller using #ModelAttribute.
public class ProductForm{
private String name;
private String label;
//getters and setters
}
Then created a class that wraps ProductForm into ArrayList
public class ProductFormArray {
ArrayList<ProductForm> forms;
//getters and setters
}
And a Controller
#Controller
#RequestMapping(value = "/products")
public class CreateAccountControllerTemporary {
#RequestMapping(value = "/get", method = RequestMethod.POST)
public String createAccount(#ModelAttribute(name = "form")ProductFormArray form){
//some code
}}
My problem is that I can't figure out how to add objects to form ArrayList using values from input fields? Is that even possible? How should I change my HTML file?
It is certainly possible, I explain this on pages 361 to 389 in my book Taming Thymeleaf.
You can check out the sources of the book for free at https://github.com/wimdeblauwe/taming-thymeleaf-sources/tree/main/chapter16
It is hard to summarize 30 pages into a stackoverflow answer, but briefly, check out:
CreateTeamFormData.java: This is similar to your ProductFormArray class. I do use an array instead of an ArrayList.
public class CreateTeamFormData {
#NotBlank
#Size(max = 100)
private String name;
#NotNull
private UserId coachId;
#NotNull
#Size(min = 1)
#Valid
private TeamPlayerFormData[] players;
TeamPlayerFormData.java: This is similar to your ProductForm class.
public class TeamPlayerFormData {
#NotNull
private UserId playerId;
#NotNull
private PlayerPosition position;
TeamController.java: This the controller that uses the CreateTeamFormData.
#GetMapping("/create")
#Secured("ROLE_ADMIN")
public String createTeamForm(Model model) {
model.addAttribute("team", new CreateTeamFormData());
model.addAttribute("users", userService.getAllUsersNameAndId());
model.addAttribute("positions", PlayerPosition.values()); //<.>
return "teams/edit";
}
#PostMapping("/create")
#Secured("ROLE_ADMIN")
public String doCreateTeam(#Valid #ModelAttribute("team") CreateTeamFormData formData,
BindingResult bindingResult, Model model) {
if (bindingResult.hasErrors()) {
model.addAttribute("editMode", EditMode.CREATE);
model.addAttribute("users", userService.getAllUsersNameAndId());
model.addAttribute("positions", PlayerPosition.values());
return "teams/edit";
}
service.createTeam(formData.toParameters());
return "redirect:/teams";
}
edit.html -> This is the Thymeleaf template. Note that I am using a Thymeleaf fragment edit-teamplayer-fragment for the part of the form that repeats itself (So the name and label fields in your case)
<h3>Players</h3>
<div class="col-span-6 ml-4">
<div id="teamplayer-forms"
th:data-teamplayers-count="${team.players.length}"> <!--.-->
<th:block th:each="player, iter : ${team.players}">
<div th:replace="teams/edit-teamplayer-fragment :: teamplayer-form(index=${iter.index}, teamObjectName='team')"></div>
<!--.-->
</th:block>
</div>
<div class="mt-4">
<a href="#"
class="py-2 px-4 border border-gray-300 rounded-md text-sm leading-5 font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800"
id="add-extra-teamplayer-form-button"
th:text="#{team.player.add.extra}"
#click="addExtraTeamPlayerForm()"
></a> <!--.-->
</div>
</div>
edit-teamplayer-fragment.html: Here is the important part where you need to keep track of the index for each fragment:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
lang="en">
<!-- tag::main[] -->
<div th:fragment="teamplayer-form"
class="col-span-6 flex items-stretch"
th:id="${'teamplayer-form-section-' + __${index}__}"
th:object="${__${teamObjectName}__}"> <!--.-->
<!-- end::main[] -->
<div class="grid grid-cols-1 row-gap-6 col-gap-4 sm:grid-cols-6">
<div class="sm:col-span-2">
<div class="mt-1 rounded-md shadow-sm">
<select class="form-select block w-full transition duration-150 ease-in-out sm:text-sm sm:leading-5"
th:field="*{players[__${index}__].playerId}">
<option th:each="user : ${users}"
th:text="${user.userName.fullName}"
th:value="${user.id.asString()}">
</select>
</div>
</div>
<div class="sm:col-span-2">
<div class="mt-1 rounded-md shadow-sm">
<select class="form-select block w-full transition duration-150 ease-in-out sm:text-sm sm:leading-5"
th:field="*{players[__${index}__].position}">
<option th:each="position : ${positions}"
th:text="#{'PlayerPosition.' + ${position}}"
th:value="${position}">
</select>
</div>
</div>
<!-- tag::delete[] -->
<div class="ml-1 sm:col-span-2 flex items-center text-green-600 hover:text-green-900">
<div class="h-6 w-6">
<svg th:replace="trash"></svg>
</div>
<a href="#"
class="ml-1"
th:text="#{team.player.remove}"
x-data
th:attr="data-formindex=__${index}__"
#click="removeTeamPlayerForm($el.dataset.formindex)"> <!--.-->
</a>
</div>
<!-- end::delete[] -->
</div>
</div>
</html>

Spring Boot Forms; hiding the path value and showing placeholder

I am having a small issue with Spring Boot forms displaying the information of the path value instead of the placeholder once you get to the editProfile.jsp. I want the input field to look like this;
Edit Profile Page instead of this Wrong Edit Profile. I do not want my users to have to click, select and delete the auto completed value. I want it to show the placeholder only and allow them to overwrite what is shown with ease.
This is the editProfile.jsp
<%--#elvariable id="editProfile" type=""--%>
<form:form method="POST" modelAttribute="editProfile">
<div class="MyForm form-group">
<h1>Edit Profile</h1>
<form:input type="email" class="MyInput" id="email" path="email" placeholder="${editProfile.email}" />
<form:button type="submit" class="from-control">Submit</form:button>
</div>
<div>
<img src="images/reg1.png" alt="picture">
</div>
</form:form>
</body>
</html>
This is the code specified in the Controller
#RequestMapping(value = "edit/{email}", method = RequestMethod.GET)
public String getEditUserData(#PathVariable("email") String email, Model model) {
AccountEntity accountInstance = accountRepo.findByEmail(email);
model.addAttribute("editProfile", accountInstance);
return "editProfile";
}
#RequestMapping(value = "edit/{email}", method = RequestMethod.POST)
public String enterEditUserData(#ModelAttribute("login") AccountEntity accountForm, #PathVariable("email") String email, Model model ) {
AccountEntity accountInstance = accountRepo.findByEmail(email);
accountInstance.setEmail(accountForm.getEmail());
accountRepo.save(accountInstance);
return "redirect:/login";
}
I have figured it out; You have to add a model of a new Entity, so the path variable does not fill in with the instance of the specific path value. Here is the new code, and compare it to the one I sent above.
#RequestMapping(value = "edit/{email}", method = RequestMethod.GET)
public String getEditUserData(#PathVariable("email") String email, Model model) {
AccountEntity accountInstance = accountRepo.findByEmail(email);
model.addAttribute("editProfile2", new AccountEntity());
model.addAttribute("editProfile1", accountInstance);
return "editProfile";
}
<%--#elvariable id="editProfile" type=""--%>
<%--#elvariable id="editProfile2" type=""--%>
<form:form method="POST" modelAttribute="editProfile2">
<div class="grid form-group">
<h1>Edit Profile</h1>
<form:input type="email" class="MyInput" id="email" path="email" placeholder='${editProfile1.email}' />
<form:button type="submit" class="from-control">Submit</form:button>
</div>

Return value from select using FreeMarker

I have a form:
<form action="user-fonts" method="post" ">
<select >
<#list fonts as font>
<option value=${font}>${font.nameFont?ifExists}</option>
</#list>
</select>
<input type="hidden" name="_csrf" value="${_csrf.token}" />
<div><input type="submit" value="Sign In"/></div>
</form>
How to get the value in the controller that I chose on the form?
#RequestMapping("/user-fonts")
public class MainController{
#GetMapping
public String main(#AuthenticationPrincipal User user, Model model)
{
Set<DBFont> fonts = user.getFont();
model.addAttribute("fonts", fonts);
return "Myfonts";
}
#PostMapping
public String mainPost(#ModelAttribute DBFont DBfont)
{
System.out.println(DBfont.getNameFont());
return "redirect:/user-fonts";
}
There is a value in the database, but I get null, How to return the value?
You need to define a name attribute to your select, e.g. nameFont:
<select name="nameFont">
This will send font selected value as POST parameter nameFont

using search functionality in thymeleaf with request parameters

I have a page where I get a list of entries. Now, I want to be able to search from those list.
my current url for retrieving list is this /show/products. I want to add a search form in this page so that I can search with request parameter.
Yes, I can use ajax but I have to do it with request parameters.
So if I search for a product name, then - /show/products?name=someName
<form ui-jp="parsley" th:action="#{/show/products(name=${pName})}" th:object="${pName}" method="get">
<div class="row m-b">
<div class="col-sm-6">
Search by Name:
<input id="filter" type="text" th:field="*{pName}" class="form-control input-sm w-auto inline m-r"/>
<button class="md-btn md-fab m-b-sm indigo">
<i class="material-icons md-24"></i>
</button>
</div>
</div>
</form>
And this is what I tried in controller:
#GetMapping("/show/products")
public String getProduct(Model model,
#RequestParam(required = false) String name,
#ModelAttribute String pName) {
List<Product> products = this.productService.getAllProducts(name)
model.addAttribute("products", products);
return "show_product";
}
I am getting this error:
Neither BindingResult nor plain target object for bean name 'pName' available as request attribute
at org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:153)
at org.springframework.web.servlet.support.RequestContext.getBindStatus(RequestContext.java:897)
You are trying to use variable pName (Model attribute) as a form object.
In your view you are passing a model attribute to form like this th:object="${pName}" but instead you need to pass a form object.
A form object is not a class but rather a simple java object (POJO). You can think of form object as your form but on server side.
Before you can use form object in your view, you need to create it and add it to the Model.
you will define it like this
public class MyFormObject{
private String pName;
public String getPName(){
return pName;
}
public void setPName(String pName){
this.pName = pName;
}
}
now your controller method will become
#GetMapping("/show/products")
public String getProduct(Model model,
#ModelAttribute("myFormObject") MyFormObject myFormObject,
BindingResult result) {
List<Product> products = this.productService.getAllProducts(myFormObject.getPName());
model.addAttribute("products", products);
return "show_product";
}
Then you can pass the form object to your form like this
<form ui-jp="parsley" th:action="#{/show/products}" th:object="${myFormObject}" method="get">
<div class="row m-b">
<div class="col-sm-6">
Search by Name: <input id="filter" type="text" th:field="*{pName}" class="form-control input-sm w-auto inline m-r"/>
<button class="md-btn md-fab m-b-sm indigo"><i class="material-icons md-24"></i></button>
</div>
</div>
</form>
You need to read the documentation, all these are explained there in detail.

java.lang.IllegalStateException: Neither BindingResult nor plain target object in ajax call

java.lang.IllegalStateException: Neither BindingResult nor plain target object in ajax call
I am using Thymeleaf and Spring MVC and I am having some problems with a dynamic form.
I have a form with a selector, and when this changed, I do an Ajax call, which should show another selector and a field.
I have these objects:
public class SaleMini{
private int businessId;
private int sellerId;
private int productId;
private int amount;
private int row;
private String date;
private List<SaleItem> item;
//getters and setters
}
public class SaleItem{
private int productId;
private int amount;
private boolean gift;
private List<Integer> components;
private List<Composition> compositionList;
//getters and setters
}
My html code is:
<form id="sales" action="#" th:action="#{/sales/add}" method="post">
<div class="row">
<div class="form-group col-md-6">
<label class="label-control" th:text="#{label.equipment}"></label>
<select th:field="${sales.businessId}" class="form-control" onchange="submitData()"> <!--- Equipment List --->
<option th:each="e : ${equipmentList}" th:value="${e.id}" th:text="${e.name}"></option>
</select>
</div>
<div class="form-group col-md-6">
<label class="label-control" th:text="#{label.seller}"></label>
<select th:field="${sales.sellerId}" class="form-control">
<option th:each="s : ${sellerList}" th:value="${s.id}" th:text="${s.name + ' ' + s.surname}"></option>
</select>
</div>
</div>
<div id="product-panel" class="row" >
<div th:fragment="resultsList">
<div th:each="i,rowStat : ${itemList}">
<p th:text="${i.productId}"></p>
<select class="form-control products_select" th:field="${i.productId}" th:onchange="${'javascript:callComposed(' + rowStat.index + ')'}" >
<option value="0" >Select Product</option>
<option th:each="p : ${productList}" th:value="${p.id}" th:text="${p.name}" th:attr="data-compound=${p.compound},data-generic=${p.genericId}"></option>
</select>
</div>
<a class="btn btn-action" id="btn-add" onclick="submitData()" style="margin-top: 25px !important;"><span class="fa fa-plus fa-btn"></span></a> <!--I should add as many product as I wanted-->
</div>
</div>
<div class="row">
<div class="form-btn">
<input type="submit" th:value="#{label.save.sale}" class="btn btn-custom"/>
</div>
</div>
</form>
When the Equipment List is change, I do an ajax call
function submitData(){
$.ajax({
'url': 'sales/addRow',
'type': 'POST',
'data': $('#sales').serialize(),
'success': function(result){
$("#product-panel" ).html( result );
},
});
}
The function I call on the controller is:
#RequestMapping(value = "/addRow", method = RequestMethod.POST)
public String addRow(#Valid SaleMini sale, BindingResult bindingResult,ModelMap model) {
List<SaleItem> siList = new ArrayList<SaleItem>();
if(sale!=null && sale.getBusinessId()!=0)
{
SaleItem si = new SaleItem();
si.setAmount(1);
siList.add(si);
}
model.addAttribute("itemList", siList);
return folder+"/add :: resultsList";
}
The problem is when I call to submitData().
I can do the call to the controller well (submitData() and then addRow), and it works, but when I get the data I have and error:
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'i' available as request attribute
I get data after the call, but I can't access to the data with th:field
In the html part this works (th:text):
<p th:text="${i.productId}"></p>
But this not (th:field), and I don't know why:
<select class="form-control products_select" th:field="${i.productId}" th:onchange="${'javascript:callComposed(' + rowStat.index + ')'}" >
</select>
Thank you in advance
Hi I think you are missing a couple of details in your form. With this th:object="sale" you say what will be the modelAttribute of your form, and to make reference to any attribute of that object just use *{attribute.path}
<form id="sales" action="#" th:action="#{/sales/add}" method="post" th:object="sale">
And to make reference to attributes of your sale object use:
<select th:field="*{businessId}" class="form-control" onchange="submitData()"> <!--- Equipment List --->
<option th:each="e : ${equipmentList}" th:value="${e.id}" th:text="${e.name}"></option>
</select>

Categories

Resources