Spring Boot Thymeleaf Method POST SELECT null - spring-boot

Guys.
Tell me please how to get values from select in the Controller?
This returns null.
request.getParameter("firstUserYears");
request.getParameter("secondUserYears");
I want to get value of "birthDay" field.
my thymeleaf html form:
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form method="post" action="/calculate_years">
<select name="firstUserYears" th:field="*{users}"/>
<option th:each="user : ${users}" th:value="firstUserYears" th:text="${user.firstName}">
</option>
</select>
<select name="secondUserYears" th:field="*{users}"/>
<option th:each="user : ${users}" th:value="${user.firstName}" th:text="${user.firstName}">
</option>
</select>
<button type="submit">Submit</button>
</form>
</body>
</html>
Controller:
#GetMapping("/user-difference")
public String calculateDifferenceForm(Model model) {
model.addAttribute("users", service.findAll());
return "user-difference";
}
#PostMapping("/calculate_years")
public String calculateDifferenceForm(HttpServletRequest request, #ModelAttribute User user,
BindingResult bindingResult){
String firstUsersYearsOld = request.getParameter("firstUserYears");
String secondUsersYearsOld = request.getParameter("secondUserYears");
String name = request.getParameter("name");
BindingResult results = bindingResult;
System.out.println(results);
System.out.println(user);
System.out.println(name);
System.out.println(firstUsersYearsOld);
System.out.println(secondUsersYearsOld);
return "redirect:/user-difference";
}

You should start with defining a form data object. For example:
public class AgeDifferenceFormData {
private long user1Id;
private long user2Id;
// getters and setters here
}
Create an empty such object in your #GetMapping:
#GetMapping("/user-difference")
public String calculateDifferenceForm(Model model) {
model.addAttribute("formData", new AgeDifferenceFormData());
model.addAttribute("users", service.findAll());
return "user-difference";
}
Now update your HTML form to use the form data object:
<form method="post" action="/calculate_years" th:object="${formData}">
<select th:field="*{user1Id}"/>
<option th:each="user : ${users}" th:value="${user.id}" th:text="${user.firstName}">
</option>
</select>
<select th:field="*{user2Id}"/>
<option th:each="user : ${users}" th:value="${user.id}" th:text="${user.firstName}">
</option>
</select>
<button type="submit">Submit</button>
</form>
Note how you need to:
Set a selected object for Thymeleaf via th:object="${formData}"
Set the dedicated field for each select via th:field="*{user1Id}" and th:field="*{user2Id}"
Use the unique id of the user for the th:value.
Now in your #PostMapping method do this:
#PostMapping("/calculate_years")
public String calculateDifferenceForm(#ModelAttribute("formData") AgeDifferenceFormData formData,
BindingResult bindingResult){
User user1 = service.getUser(formData.getUser1Id());
User user2 = service.getUser(formData.getUser2Id());
// calculate age difference here
return "redirect:/user-difference";
}
See also Using HTML select options with Thymeleaf for more info.

Related

Is it possible to use Spring model in Javascript functions inside Thymeleaf template?

I've the following domain:
#Document(collection = "backupareas")
public class BackupArea {
#Id
private String id;
private String area;
private List<Tape> tapes;
In my template I would that when I change area a js function fill the tape select with related area tapes.
<div class="form-group col-md-3">
<label for="backup"><i>*</i> Backup</label>
<select id="backup" class="form-control" name="backup" required onchange="loadTapes();">
<option value="" selected="selected">--- Select Area ---</option>
<option th:each="area: ${areas}" th:value="${area.getArea()}" th:text="${area.getArea()}"></option>
</select>
</div>
<div class="form-group col-md-3">
<label for="tape"><i>*</i> Tape</label>
<select id="tape" class="form-control" name="tape" required >
</select>
I start with this js function, but I don't know how to use (or if it is possible) model variables.
function loadTapes() {
var area = $("#backup").val();
console.log($("#backup").index(area));
if($("#backup").index(area) == 1) {
$("#tape").empty();
return false;
}
$("#tape").empty();
var select = $('#tape');
select.append($("<option />").val("").text("--- Select one ---"));
// Here should use model variable to loop over tapes related to the selected area
select.append($("<option/>").val(TAPE).text(TAPE));
}
I solved. I created a fragment as:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns="http://www.w3.org/1999/xhtml">
<head>
</head>
<body>
<th:block th:fragment="tapes">
<th:block th:if="${tapes != null}">
<option th:each="tape: ${tapes}" th:value="${tape}" th:text="${tape}"></option>
</th:block>
</th:block>
</body>
</html>
In the main template I call an ajax method:
function loadTapes() {
$("#tape").empty();
$.post("/area/loadTapes", {area: area}, function (data) {
$('#tape').append(data);
});
}
The loadTapes method is:
PostMapping("/area/loadTapes")
public String loadTape(#RequestParam("area") String area, Model model) {
BackupArea backupArea = backupAreaService.findByArea(area);
List<Integer> tapes = new ArrayList<>();
for(Tape tape: backupArea.getTapes()) {
tapes.add(tape.getTape());
}
model.addAttribute("list", tapes);
return "/backup/tapes :: list";
}

Thymeleaf provide null in controller

I want to deliver the entire object from html to the controller
Controller:
#RequestMapping(method = RequestMethod.GET)
public String get(){
Info info = new Info();
info.setTitle("Hello");
model.addAttribute("infos", Collections.singleton(info));
return "info-page";
}
#RequestMapping(method = RequestMethod.POST, value = "show-all")
public String showAllInfoObject(#ModelAttribute("info") Info info){
// info has null values!
}
HTML
<li th:each="info : ${infos}">
<span th:text="${info.title}">webTitle</span>.
<form th:action="#{'/show-all}" method="post">
<input type="hidden" name="result" th:field="*{info}" />
<input type="submit" value="show detalis" />
</form>
</li>
However, the controller gets an empty object.
Interestingly, when I provide only String "title", it gets the correct result in the controller.
How to deliver correctly the whole object from HTML?
The problem is that th:field replaces the attributes value, id, and name in the input tag.
I would rewrite the HTML code as something like this:
<ul>
<li th:each="info : ${infos}">
<span th:text="${info.title}"></span>
<form th:action="#{/show-all}" method="post" >
<input type="hidden" th:value="${info.title}" name="title" id="title" />
<input type="submit" value="show detalis" />
</form>
</li>
</ul>
So, setting the input name and id with "title", the controller will make the bind as expected.
The controller code remains the same but I will leave here the test that I did.
#RequestMapping(method = RequestMethod.GET)
public String get(Model model){
Info info = new Info();
info.setTitle("Hello");
Info info2 = new Info();
info2.setTitle("Hello2");
model.addAttribute("infos", Arrays.asList(info, info2));
return "info-page";
}
#RequestMapping(method = RequestMethod.POST, value = "show-all")
public String showAllInfoObject(#ModelAttribute("info") Info info){
// info has values!
return "info-page";
}
Hope it helps you.

Java list items re-added to thymeleaf dropdown on page reload, appearing multiple times

I am building a Spring Boot app that calculates and displays Airbnb payouts on a monthly basis. Payout data is pulled in from the Airbnb Api, and user account information is stored in a database.
I have created a form where the user can specify the month and the listing to display the monthly payout. The user chooses the listing (the rental) from a dropdown menu. To display the names of the listings, a listingListDto attribute is added to the MVC model. A List of Listing entities is obtained from the database, and it is converted to a List of ListingDTO entities. This list gives the listingListDto.
When I reload the form page, the listings are readded to the dropdown, appearing twice, then three, four and more times.
How could I prevent this from happening?
I assume I could create a ListingListDTO entity, which would wrap the List of ListingDTOs, but I was hoping to be able to keep things simple, and use the List of ListingDTOs directly in the MVC model.
Here is the Controller method that displays the html form :
#RequestMapping(value = "payout", method = RequestMethod.GET)
public String payoutSelection(Model model, RedirectAttributes redirectAttributes) {
Long userId = sessionService.getCurrentUserId();
if (null == userId) {
return loginService.handleInvalidLogin("payout", redirectAttributes);
} else {
PayoutSelectionDTO payoutSelectionDto = new PayoutSelectionDTO();
LocalDate lastMonth = LocalDate.now().minusMonths(1);
payoutSelectionDto.setYear(lastMonth.getYear());
payoutSelectionDto.setMonth(lastMonth.getMonthValue());
Optional<User> user = userService.getUserById(userId);
model.addAttribute("payoutSelectionDto", payoutSelectionDto);
model.addAttribute("listingListDto", listingListDtoService.getListingListDTO(listingService.getListingsByUser(user.get())));
return "payout_monthpicker.html";
}
}
Here is the form which contains the dropdown of listings:
<body>
<div class="content-block">
<form action="#" th:action="#{/get_payouts}"
th:object="${payoutSelectionDto}" method="POST">
<h2>Kifizetések lekérése</h2>
<div class="content-group">
<select th:field="*{year}">
<option th:value="${payoutSelectionDto.year} -1"
th:text="${payoutSelectionDto.year} -1"></option>
<option th:value="*{year}" th:text="*{year}"></option>
</select> <select th:field="*{month}">
<option th:value="'1'" th:text="Január"></option>
<option th:value="'2'" th:text="Február"></option>
<option th:value="'3'" th:text="Március"></option>
<option th:value="'4'" th:text="Április"></option>
<option th:value="'5'" th:text="Május"></option>
<option th:value="'6'" th:text="Június"></option>
<option th:value="'7'" th:text="Július"></option>
<option th:value="'8'" th:text="Augusztus"></option>
<option th:value="'9'" th:text="Szeptember"></option>
<option th:value="'10'" th:text="Október"></option>
<option th:value="'11'" th:text="November"></option>
<option th:value="'12'" th:text="December"></option>
</select>
</div>
<div class="content-group">
<select th:field="*{listingId}">
<option th:each="listingDto : ${listingListDto}" th:value="${listingDto.airbnbId}" th:text="${#strings.abbreviate(listingDto.airbnbLabel,30)}"></option>
</select>
</div>
<div class="content-group">
<button type="submit" class="btn btn-primary btn-lg btn-block">Lekérés indítása</button>
</div>
</form>
</div>
</body>
Here is the ListingListDtoService. It has a way of screening for duplicates, so unless this is not doing what I think it is doing, no duplicates should be there from the result of running this service.
#Service
public class ListingListDTOService {
List<ListingDTO> listingDtoList;
public ListingListDTOService() {
this.listingDtoList = new ArrayList<>();
}
public List<ListingDTO> getListingListDTO(List<Listing> listingList) {
for(Listing listing : listingList) {
ListingDTO listingDto = convertListingToListingDTO(listing);
if(!listingDtoList.contains(listingDto)) {
listingDtoList.add(listingDto);
}
else {
System.out.println("identical Dto found in list while adding Dtos.");
}
}
return listingDtoList;
}
public ListingDTO convertListingToListingDTO(Listing listing) {
ListingDTO listingDto = new ListingDTO();
listingDto.setAirbnbId(listing.getAirbnbId());
listingDto.setAirbnbLabel(listing.getAirbnbLabel());
listingDto.setAirbnbPictureUrl(listing.getAirbnbPictureUrl());
return listingDto ;
}
}
Thanks to the coments from #Seth, this problem has been resolved.
Just copying from the comments to answer this.
If you don't have a good equals()/hashcode() method on ListingDTO, then your contains() call won't do what you want.

how to pass checkbox value from jsp to java class

I have in my jsp page some checkbox and I want to pass in my spring controller the checked ones.
insertTaskInformation.jsp
<body>
<div align="center">
<strong> <strong>Title:</strong>${task.title} <form:form
action="addSymbol" method="post" name="catch">
<c:forEach var="symbol" items="${symbols}">
<input type="checkbox" name="id" value="${symbol.type} }">${symbol.type}<BR>
</c:forEach>
<input type="submit" value="Submit">
</form:form>
</strong>
</div>
</body>
taksController.java
#RequestMapping(value="/addSymbol", method = RequestMethod.POST)
public String addSymbol() {
return "administration/taskRecap";
}
You can use spspring:form library and you add in your controller something like this :
#PostMapping("/greeting")
public String greetingSubmit(#ModelAttribute Greeting greeting) {
return "result";
}
And In your first controller, you add an attribute to the model with model.addAttribute(new greeting() ) so the full code will be
#RequestMapping(value="/addSymbol", method = RequestMethod.POST)
public String addSymbol(Model model) {
model.addAttribute(new greeting() )
return "administration/taskRecap";
}
For more information check here

Why I am getting null when I send a request to the controller in spring MVC?

I send a list to my jsp page which gives selection to the user. This part is doing fine. But when the request is send back to the controller is null. I am going to get the request by #ModelAttribute in the controller but the object is null. Jsp and controller are as bellow:
<form action="deleteAuthorExecution" method="post">
Select Author: <select name="author">
<option value="all" label="All Authors">All Authors</option>
<c:forEach var="author" items="${authorList}">
<option value="${author}">${author}</option>
</c:forEach>
</select>
<input type="submit" value="submit"/>
</form>
#RequestMapping(value="/deleteAuthorExecution", method = RequestMethod.POST)
protected ModelAndView deleteAuthorExecution(#ModelAttribute Author author) throws Exception {
authorService.delAuthor(author.getAuthorId());
ModelAndView model = new ModelAndView("adminFunctionsPage");
model.addObject("successMsg", "Your request has been processed Successfully.");
return model;
}
the author object coming null. Can anybody give me a hint what is the problem in my code?
I would suggest instead of sending the whole author object, you can pass authorId
<form action="deleteAuthorExecution" method="post">
Select Author: <select name="author">
<option value="0" label="All Authors">All Authors</option>
<c:forEach var="author" items="${authorList}">
<option value="${author.authorId}">${author.authorName}</option>
</c:forEach>
</select>
<input type="submit" value="submit"/>
</form>
#RequestMapping(value="/deleteAuthorExecution", method = RequestMethod.POST)
protected ModelAndView deleteAuthorExecution(#RequestParam Integer authorId) throws Exception {
if(authorId == 0){
//user selected all, so delete all the authors
}
authorService.delAuthor(authorId);
ModelAndView model = new ModelAndView("adminFunctionsPage");
model.addObject("successMsg", "Your request has been processed Successfully.");
return model;
}
Hope this helps

Resources