Spring Boot & Thymeleaf,passing variables to the controller - java

I have a controller that processes the purchase of a product,it takes two parameters,count - the quantity of the product and id - the ID of the product that the user buys.
#PostMapping("/buyproduct")
public String buyProduct(#RequestParam int count,#RequestParam long id){
Product product = productRepository.findById(id);
int activeCount = product.getCount();
if (activeCount-count<0){
return "redirect:/";
}
product.setCount(activeCount-=count);
productRepository.save(product);
return "redirect:/buyproductsuccessful";
}
In index.html I have listed all available products in the database using Thymeleaf.
<div class="col-12 col-md-6 col-lg-4" th:each="element : ${products}">
<div class="card">
<img class="card-img-top" src="https://picsum.photos/362/180" alt="Card image cap">
<div class="card-body">
<h5 style="text-align: center" class="card-title" th:text="${element.getName()}"</h5>
<span th:text="${element.getCost()}"></span></p>
<span th:text="${element.getCount()}"></span> </p>
...
Each product has its own button,when clicked, a popup opens in which you need to enter the quantity of the purchased product and Bank data (I don't process them, I don't Need them yet), and there is also a hidden field in which I want to put the product ID so that it can be passed to the controller later.
<form action="#" th:action="#{/buyproduct}" th:object="${element}" method="post">
<!-- Modal Header -->
<div class="modal-header">
<h4 class="modal-title" style="color: #368819"></h4>
<button type="button" class="close" data-dismiss="modal">×</button>
</div>
<!-- Modal body -->
<div class="modal-body">
<div>
<input type="hidden" name="id" th:value="*{getId()}">
<label><b>რაოდენობა</b></label><br>
<input class="col-4" type="number" min="1" value="1" name="count" required><br><br>
<label><b></b></label><br>
<input class="col-12" type="text"><br><br>
<label><b></b></label><br>
<input class="col-12" type="date"><br><br>
<label><b>CVV/CVC</b></label><br>
<input class="col-4" type="text"><br><br>
</div>
</div>
<!-- Modal footer -->
<div class="modal-footer">
<button type="submit" class="btn btn-success"></button>
<button type="button" class="btn btn-danger" data-dismiss="modal"></button>
</div>
</form>
But Thymeleaf passes the id of the first product every time,what is my mistake?

The problem was with Bootstrap modal scopes.
<button style="margin-left: 40%" type="button" class="btn btn-success" data-toggle="modal" data-target="#myModal">
<div class="modal" id="myModal">
After fix its looks like this.
<button style="margin-left: 40%" type="button" class="btn btn-success" data-toggle="modal" th:attr="data-target='#id-' + ${element.getId()}">
<div class="modal" th:id="'id-' + ${element.getId()}">

Related

Spring-Boot - Redirect to an url with an ID in it

I do a simple vocabulary application as a practice. It can insert new vocabularies and then open that vocabulary so you can add some words in it.
My goal is to insert those words with a modal and with the button Save redirect to that current vocabulary's url.
So create a new Vocabulary and with an "Open" button you go to localhost:8080/vocabulary/1 where you can add new words with a modal, and then with the Save button you redirect back to that URL. (vocabulary/{id})
My question is: Could anyone recommend a solution? I have tried to find something, but it doesn't work.
I tried with this:
Controller:
#PostMapping("/vocabulary/{id}")
public ModelAndView addWord(#RequestBody Words word, #PathVariable(name="id") Integer id) {
ModelAndView mav = new ModelAndView("redirect:/vocabulary");
wordService.addWord(word);
List<Words> wordList = wordService.getWordsByVocabulary(id);
mav.addObject("listOfWords", wordList);
return mav;
}
HTML-Modal
<div class="modal fade" id="createModal" tabindex="-1" role="dialog" aria-labelledby="createModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="createModalLabel">New Word</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form th:object="${word}" method="post" th:action="#{'/vocabulary/' + ${vocId}}">
<div class="form-group">
<label for="english" class="col-form-label">English:</label>
<input type="text" class="form-control" id="english" th:field="*{english}">
</div>
<div class="form-group">
<label for="hungarian" class="col-form-label">Hungarian:</label>
<input type="text" class="form-control" id="hungarian" th:field="*{hungarian}">
</div>
<input type="hidden" th:field="*{vocabularyId}" id="vocabularyId">
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Save</button>
</div>
</form>
</div>
</div>
</div>
</div>
The Save button starts the Controller's addWord() method.

NoSuchElementException - junit test error

I am using selenium junit testing.
If i try with selenium ide, the test goes well, but with junit it gives me this error:
org.openqa.selenium.NoSuchElementException: no such element: Unable to locate element: {"method":"css selector","selector":"#firstName"}
I think that the span doesn't redirect to the page it should (but in normal schedule it does).
#Test
public void adminCreateDoc() {
driver.get("http://localhost:8080/login");
driver.manage().window().setSize(new Dimension(550, 706));
driver.findElement(By.linkText("Accesso amministratori")).click();
driver.findElement(By.id("username")).sendKeys("8245");
driver.findElement(By.id("password")).click();
driver.findElement(By.id("password")).sendKeys("prova1");
driver.findElement(By.cssSelector(".btn")).click();
driver.findElement(By.id("btn_createDoc")).click();
driver.findElement(By.id("firstName")).click();
driver.findElement(By.id("firstName")).sendKeys("Marco");
driver.findElement(By.id("lastName")).click();
driver.findElement(By.id("lastName")).sendKeys("Battiato");
driver.findElement(By.id("doc_type")).click();
driver.findElement(By.id("doc_type")).sendKeys("Cardiologo");
driver.findElement(By.id("id")).click();
driver.findElement(By.id("id")).sendKeys("855555");
driver.findElement(By.id("password")).click();
driver.findElement(By.id("password")).sendKeys("prova1");
driver.findElement(By.cssSelector(".glyphicon-send")).click();
}
I've tried with xpath, but without success...
HTML page with span redirection:
<body>
<div class="container text-center">
<div align="center">
<h2>Gestione dottori</h2>
<table class="table table-striped table-responsive-md">
<tr>
<th>ID</th>
<th>Nome</th>
<th>Specialit&agrave</th>
</tr>
<tr th:each="doc: ${listDoc}">
<td th:text="${doc.getId()}"></td>
<td>Dr. <span th:text="${doc.getFirstName()}"></span> <span th:text="${doc.getLastName()}"></span></td>
<td th:text="${doc.getDoc_type()}"></td>
</tr>
</table>
<a th:href = "#{/admin_createdoc}"><span id="btn_createDoc" class="plus bg-dark" >+</span></a>
<hr>
<div class="col col-lg-2 align-self-center">
<div class="p-1">
<form th:action="#{/admin_logout}" method=post>
<button name="btn_logout_profile" id="btn_logout_profile"
type="submit" class="btn btn-primary">Log Out</button>
</form>
</div>
</div>
</div>
</div>
</body>
Which redirect to this page:
<body>
<div class="container">
<div class="d-flex align-items-center justify-content-start">
<form th:action="#{/admin_homepage}" method=get>
<button name="btn_back_profile" id="btn_back_profile" type="submit"
class="btn btn-info">
<span class="fas fa-chevron-left"></span>
</button>
</form>
</div>
<br>
<div style='text-align: center'>
<form class="well form-horizontal" action="#"
th:action="#{/saveDoctor}" th:object="${doc}" method="POST"
id="contact_form">
<fieldset>
<!-- Form Name -->
<legend>
<center>
<h2>
<b>Doctor Creation Form</b>
</h2>
</center>
</legend>
<br>
<!-- Text input-->
<div class="form-group">
<label class="col-md-4 control-label">First Name</label>
<div class="col-md-4 inputGroupContainer">
<div class="input-group">
<span class="input-group-addon"><i
class="glyphicon glyphicon-user"></i></span> <input name="first_name"
th:field="*{firstName}" placeholder="Doc First Name"
class="form-control" type="text" required>
</div>
</div>
</div>
<!-- Text input-->
<div class="form-group">
<label class="col-md-4 control-label">Last Name</label>
<div class="col-md-4 inputGroupContainer">
<div class="input-group">
<span class="input-group-addon"><i
class="glyphicon glyphicon-user"></i></span> <input name="last_name"
th:field="*{lastName}" placeholder="Doc Last Name"
class="form-control" type="text" required>
</div>
</div>
</div>
<div class="form-group">
<label class="col-md-4 control-label">Type</label>
<div class="col-md-4 inputGroupContainer">
<div class="input-group">
<span class="input-group-addon"><i
class="glyphicon glyphicon-list"></i></span> <input name="doc_type"
th:field="*{doc_type}" placeholder="Doc Type"
class="form-control" type="text" required>
</div>
</div>
</div>
<!-- Text input-->
<div class="form-group">
<label class="col-md-4 control-label">Id</label>
<div class="col-md-4 inputGroupContainer">
<div class="input-group">
<span class="input-group-addon"><i
class="glyphicon glyphicon-user"></i></span> <input name="id"
th:field="*{id}" placeholder="Doc ID" class="form-control"
type="number" required>
</div>
</div>
</div>
<!-- Text input-->
<div class="form-group">
<label class="col-md-4 control-label">Password</label>
<div class="col-md-4 inputGroupContainer">
<div class="input-group">
<span class="input-group-addon"><i
class="glyphicon glyphicon-user"></i></span> <input
th:field="*{password}" name="doc_password"
placeholder="Doc Password" class="form-control" type="password"
required>
</div>
</div>
</div>
<!-- Button -->
<div class="form-group" align="center">
<label class="col-md-4 control-label"></label>
<div class="col-md-4">
<br>
<button type="submit" class="btn btn-warning">
SUBMIT <span class="glyphicon glyphicon-send"></span>
</button>
</div>
</div>
</fieldset>
</form>
<div>
<form th:action="#{/admin_logout}" method=post>
<button name="btn_logout_profile" id="btn_logout_profile"
type="submit" class="btn btn-primary">Log Out</button>
</form>
</div>
</div>
</div>
</body>
Thank you for your time!
The failure is happening on this line
driver.findElement(By.id("firstName")).click();
ID as a CSS selector is #firstName. Since this is right after a page transition, my guess is that the page isn't fully loaded before the next line of code is run. This can sometimes happen on a modern site where the page is loaded but there's still stuff being loaded asynchronously in the background. The fix is to add a wait, specifically a WebDriverWait, to the following line. That is one option...
Instead of adding a wait piecemeal when it's found that you need it, I prefer to write helper methods that take care of common actions like click(), sendKeys(), etc. and then let those methods take care of the specific waits for me.
public void click(By locator) {
new WebDriverWait(driver, 10).until(ExpectedConditions.elementToBeClickable(locator)).click();
}
public void sendKeys(By locator, String text) {
findElement(locator).sendKeys(text);
}
public WebElement findElement(By locator) {
return new WebDriverWait(driver, 10).until(ExpectedConditions.visibilityOfElementLocated(locator));
}
Then you can change your script to
#Test
public void adminCreateDoc() {
driver.get("http://localhost:8080/login");
driver.manage().window().setSize(new Dimension(550, 706));
click(By.linkText("Accesso amministratori"));
sendKeys(By.id("username"), "8245");
sendKeys(By.id("password"), "prova1");
click(By.cssSelector(".btn"));
click(By.id("btn_createDoc"));
sendKeys(By.id("firstName"), "Marco");
sendKeys(By.id("lastName"), "Battiato");
sendKeys(By.id("doc_type"), "Cardiologo");
sendKeys(By.id("id"), "855555");
sendKeys(By.id("password"), "prova1");
click(By.cssSelector(".glyphicon-send"));
}
which I think makes it more readable, adds waits to everything in case it's needed with no extra code, and so on...
The next level is using the Page Object Model to contain the locators and methods for each page. Then your code gets cleaned up significantly, makes it near-human readable, and MUCH easier to manage.

Get value from modal window

I have a problem with get a value of field from modal window.
<div class="modal fade" id="addModal" tabindex="-1" role="dialog" aria-labelledby="addModal" aria-hidden="true">
<form th:action="#{/groups/add}" method="post">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="addModal">Add group</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="form-group">
<label for="recipient-name" class="col-form-label">Group Name</label> <input type="text"
class="form-control" id="addName" name="name">
</div>
<div class="form-group">
<label for="recipient-name" class="col-form-label">Faculty</label> <select
class="form-control">
<option th:value=0>Faculty not assign</option>
<option th:each="faculty : ${faculties}" th:value="${faculty.id}"
th:utext="${faculty.name}">Faculty</option>
</select>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Save</button>
</div>
</div>
</div>
</form>
</div>
In browser when I use "add" method, I can choose any Faculty from table, it works OK, but when I press button "save", only name returned to Controller and no info about faculty that was chosen. What I missed?
Finally I solved this problem- I forget to give a name for field where facultyId is stored.
Here was error:
<label for="recipient-name" class="col-form-label">Faculty</label>
<select class="form-control">
Correct:
<label for="faculyId" class="col-form-label">Faculty</label>
<select class="form-control" id="addFaculty" name="facultyId">

Why invalid-feedback in thymeleaf not working

I required input it's work but why invalid-feedback not show in modal
HTML use thymeleaf
<!-- AddStore Modal -->
<div class="modal fade" id="AddUserModal" tabindex="-1" role="dialog" aria-labelledby="deleteModalLabel"
aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="AddUserModalLabel">Add Store Form</h5>
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form autocomplete="off" action="#" th:action="#{/stores/add_store}"
th:object="${store}" method="post"
role="form" id="addModalForm">
<div class="form-row">
<div class="form-group col-md-6">
<label for="addstoreName">Store Name</label>
<input type="text" class="form-control" id="addstoreName" th:field="*{storeName}"
required="required"/>
<div class="invalid-feedback">
Please provide a store name.
</div>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<label for="addStatus">Status</label>
<select id="addStatus" class="form-control" th:field="*{status}">
<option th:value="Ready_to_service">Ready to service</option>
<option th:value="Temporarily_closed">Temporarily closed</option>
<option th:value="Closed">Closed</option>
</select>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
<button class="btn btn-success" type="submit" th:text="Save"></button>
</div>
</form>
</div>
</div>
</div>
</div>
Not sure if you found the solution or not as it looks like an old question.
You are missing to let Thymeleaf know about the field specific error. You havn't bind the invalid-feedback class to the "field" error. Try below piece of code.
<input type="text" class="form-control" id="addstoreName" th:field="*{storeName}" required="required"/>
<div class="invalid-feedback" th:if="${#fields.hasErrors('storeName')}" th:errors="*{storeName}"></div>
Take a look at the working example here. Here it will look like this:

Null value is getting passed to the Servlet from my jQuery script

I am passing a value from a jsp page to bootstrap modal using jQuery and it is working. I want to pass the same value from bootstrap modal to the servlet. I tried with ajax, post, nothing seems to be working. Here is my html:
<div class="container" style="padding-top: 60px;" >
<select class="createAlarm" id="createAlarm" name="metrics">
<option value="cpuUsage">CPU Usage</option>
<option value="memoryUsage">Memory Usage</option>
<option value="diskRead">Disk Read</option>
<option value="diskWrite">Disk Write</option>
<option value="diskUsage">Disk Usage</option>
<option value="netUsage">Net Usage</option>
</select>
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#myModal" data-whatever="Create Alarm">Create Alarm</button>
</div>
<!-- Modal- Create Alarm -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title">Create Alarm</h4>
</div>
<form action="/ProjectName/createAlarm" method="post" id="addcard" class="form-horizontal" role="form">
<div class="modal-body" style="height: 170px;">
<div class="form-group" style="height: 30px;">
<label for="title" class="col-md-3 control-label">Name</label>
<div class="col-md-9">
<input type="text" class="form-control" name="alarmName">
</div>
</div>
<div class="form-group" style="height: 30px; margin-bottom:30px;">
<label for="title" class="col-md-3 control-label">Description</label>
<div class="col-md-9">
<textarea class="form-control" name="desc"></textarea>
</div>
</div>
<div class="form-group">
<label for="priority" id="priority" name="priority" class="col-md-3 control-label">
<input type="text" class="form-control" name="name">
</label>
<div class="col-md-9">
<div class="col-md-3">
<select name="sign" class="form-control">
<option value="lessthan"><</option>
<option value="greaterthan">></option>
<option value="lessthanequalto"><=</option>
<option value="greaterthanequalto">>=</option>
</select>
</div>
<div class="col-md-3">
<input type="text" class="form-control" name="threshold">
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button class="btn btn-primary" id="formSubmit" type="submit">Create Alarm</button>
</div>
</form>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
Here is my jQuery part:
<script>
$('#myModal').on('show.bs.modal', function(e) {
var metrics_key = $('.createAlarm').val();
var metrics_label = $(".createAlarm option:selected").text();
// Now you have the key and label in variables.
// Write the key into the textfield.
$('#myModal input[name="name"]').val(metrics_key);
// Change the HTML of an element to the label.
$('#myModal label[for="priority"]').text(metrics_label);
$.post("/CMPE283_Project2/createAlarm", { val : $("#priority").text()});
alert($('#priority').text());
});
My Servlet:
String Metric = request.getParameter("val");
But, I am getting null for the String Metric. But alert pop up after clicking 'Create Alarm' button shows the correct value I want to pass the servlet. Please help. Thanks.
Your post looks ok, I believe it's just a matter of jquery syntax for accessing controls.
In particular, I'd expect a text input with id="priority" (not just a label as it appears in your code), and then you access it with '.val()' and not 'text()'
<input type="text" id="priority" ... />
$.post('your url', {priority: $("#priority").val()} ...
At least this works for me (jquery version 2.1.3). If you want to make sure, you can just start by posting hard coded data such as {priority: '7'}

Categories