I am trying to fire onClick event and Spring controller one by one.
In the click event of the button, I set a hidden field and then I want the controller to be executed AND the hidden field to be available in it. Here is my code for the JSP and the Spring Controller
JSP
<html:hidden property="MenuID" name="MenuID" id="hidMenuID" />
<c:forEach items="${menus}" var="menu" >
<tr class="cartItem">
<td align="left" >${MenuId}</td>
td align="right"><input type="text" name="menu_name" value="${Name}"/></td>
<td align="right"><input type="text" name="menu_price" value="${MenuPrice}"/></td>
<td align="right"><input type="submit" class="button" name="add_menu" value="Add" onclick="return SetMenuID();"></td>
<td align="right"><input type="submit" class="button" name="remove_menu" value="Remove" onclick="return SetMenuID();"></td>
</tr>
</c:forEach>
JavaScript
<script type="text/javascript">
function SetMenuID()
{
var MenuID = document.getElementById('hidMenuID');
MenuID.value = 'The new value';
return true;
}
</script>
Spring MVC
#RequestMapping(params = "add_menu", method = RequestMethod.POST)
public ModelAndView AddMyMenu(#RequestParam("MenuID") String menu_id, Model model, #ModelAttribute("cart") ArrayList<Menu> mycart)
{
int nMenuId = Integer.parseInt(menu_id);
Menu menu = m_arrMenus.get(nMenuId);
model.addAttribute("menus", GetMenus());
mycart.add(menu);
return new ModelAndView("edit_menu");
}
As you might guess, I am populating data from database. I have an onClick function associated with each ADD button. I set hidden field in thid function and want it to be available in my controller. But a runtime exception comes that
Error Status 400 - Required string MenuID is not present
What should I do to achieve similar result ? Thanks for any input.
#RequestParam expects a parameter my name in the request url.
Since your form is submitted and there are no request param that the action recieves. This is a reason you are getting 400 http error from server.
See the difference between #RequestParam vs #PathVariable
So either change the #RequestParam annotation or add (required = false) to it.
Related
How do you send a value of a button to a Controller?
I have the following HTML
<form th:method="POST" th:action="#{'/bet/addGame/{gameId}'
(gameId = ${game.getGameId()})}">
<th scope="row"><button class="btn btn-primary" name="konTip" value="1" th:text="${game.getCoef1()}">1.15</button></th>
<th scope="row"><button class="btn btn-primary" name="konTip" value="X" th:text="${game.getCoefx()}">4.20</button></th>
<th scope="row"><button class="btn btn-primary" name="konTip" value="2" th:text="${game.getCoef2()}">3.40</button></th>
</form>
As you can see from the image I have three buttons under the "1, X and 2" row I want to pass the value of the button on which the user clicks to the Controller on the Back-end say like a RequestParam for example but I have no idea how to do this.
I tried to add JS to it but I can't really understand the thymeleaf documentation very well
This is my Controller Class
#PostMapping("/addGame/{gameId}")
public String addGameToTicket(#PathVariable Integer gameId, Model model, HttpServletRequest request) {
User user = (User) request.getSession().getAttribute("user");
Ticket ticket = this.ticketService.findTicketByUser(user.getUsername())
.orElseThrow(()->new TicketNotFoundUserNameException(user.getUsername()));
Game gameToAdd = this.gameService.findById(gameId);
this.ticketService.addGame(user.getUsername(), gameId);
return "redirect:/bet";
}
In workers.jsp I have:
....
<form action="/workers" id="personForm" method="post" >
<c:forEach items="${page.content}" var="row" varStatus="status">
<tr>
<td><input type="checkbox"/></td>
<td>${row.name}</td>
...
<td>
<input type='image' src='/pages/img/del.png' />
<input type='hidden' name="removeid" value='${row.id}'/>
</td>
</tr>
</c:forEach>
</form>
When I click on input, ${row.id} goes to:
#RequestMapping(value = "/workers", method = RequestMethod.POST)
public ModelAndView getAllByPage(#ModelAttribute("removeid") Long removeid, Model uiModel, Pageable pageable) {
if(removeid!=null) {
userService.remove(removeid);
}
...
}
But removeid in this case is always the first, that was added to the jsp page.
Besides that, how to get the array of ids, using jsp to remove many items?
Help me, please.
You can write 1 separate REST end point for deleting multiple items.
Example:
public List<Long> deleteMultiple(List<Long> toBeDeleted){
deleteService(toBeDeleted);
return toBeDeleted;
}
You can call this End point via AJAX with multiple ides & refresh your page.
I want to update some user’s data and have issue with receiving parameters from JSP dropdown menu. I want to receive entered compId from “Enter PC” block and pass it as a PathVariable. But it is not seen. If I hardcode action="${app}/adminEdit.do/${user.userId}/${any number}" it works. So, question is – now to get this parameter from dropdown and set it to path? Thanks in advance.
Update.jsp snippet
<c:set var="app" value="${pageContext.request.contextPath}"/>
............
<DIV class="admin_redaction_block">
<sf:form name="adminUserUpdate"
method="POST"
modelAttribute="userForm"
action="${app}/adminEdit.do/${user.userId}/${comp.compId}"
enctype="application/x-www-form-urlencoded">
<c:if test="${not empty errorMsg}">
<div class="error">
<c:out value="${errorMsg}"/>
</div>
</c:if>
<sf:label path="password"><strong>Enter new password:</strong></sf:label> <br>
<sf:input path="password" type="text" size="20"/><br>
<sf:errors path="password" cssClass="error"/>
<br>
<sf:label path="email"><strong>Enter new Email:</strong></sf:label> <br>
<sf:input path="email" type="text" size="20"/><br>
<sf:errors path="email" cssClass="error"/>
<strong>PC Assigned:</strong>
<h3 class="h3">
<td>
<c:choose>
<c:when test="${user.computers!= null && !user.computers['empty']}">
<c:forEach items="${user.computers}" var="comp">
<c:out value="${comp.pcName}"/>
</c:forEach>
</c:when>
<c:otherwise>
<p class="h3_error">No PC Assigned</p>
</c:otherwise>
</c:choose>
</td>
</h3>
<sf:label path="computers">Enter PC:</sf:label> <br>
<sf:select path="computers" size="3">
<c:forEach items="${computers}" var="comp">
<sf:option value="${comp.compId}">
<c:out value="${comp.compId}"/>
</sf:option>
</c:forEach>
</sf:select>
<br> <br>
<input type="SUBMIT" name="SUBMIT" value="Update User"/>
</sf:form>
Controller
#RequestMapping(value = "/adminEdit.do/{userId}/{compId}", method = RequestMethod.POST)
public ModelAndView updateUserProcess(#ModelAttribute(value = "userForm")
UserForm userForm,
#PathVariable("userId") Integer userId,
#PathVariable("compId") Integer compId,
BindingResult result, Model model,
HttpSession session,
HttpServletRequest request) {
User user = userService.getUserById(userId);
model.addAttribute("computers", computerService.getAllComputers());
............
model.addAttribute("userForm", userForm);
return updatingUser(user, model, userForm);
}
You cannot.
You simply forgot that thing are written at different time.
<sf:form name="adminUserUpdate" ...
action="${app}/adminEdit.do/${user.userId}/${comp.compId}" ...>
is written at the time of answering the request that generates the form. At that time, your app (server side) is simply generating a HTML page, and the $comp.compid} does not exist. You can verify it by looking at the HTML source code of the page in your browser.
Later, when you click on the submitbutton, the browser gather data from input fields encode all and send it via a POST request to the action URL without changing it. Browser does not even know that you wrote ${app}/adminEdit.do/${user.userId}/${comp.compId} in your jsp : it only recieved a plain text string localhost:8080/adminEdit.do/2/
So ... try to get comp.compid from an input field of your form using a <sf:select> or <sf:checkboxes> tag.
Well, after long time of searching I've found now I can pass parameters from JSP to Controller. There are special class CustomCollectionEditor which helps pass even multiple select values.
Here is good example https://blog.codecentric.de/en/2009/07/multiple-selects-mit-spring-mvc-2/
And my snippet:
#InitBinder("userForm")
private void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Set.class, "computers", new CustomCollectionEditor(Set.class) {
#Override
protected Object convertElement(Object element) {
String pcName = null;
Set<Computer> computerSet = new LinkedHashSet<>();
if (element instanceof String && !((String) element).equals("")) {
pcName = (String) element;
}
return pcName != null ? computerService.getComputerByName(pcName) : null;
}
});
}
This question is related to this. But since I haven't solved that question yet, I want to restate my problem a bit. I'm using Java Jersey REST API as my backend web service. The client side is a simple web form HTML page. Basically what I want is: If the user submits a web form and there are some errors caused by database unique constraint violation, I want the user to see an error message showing along with the id field in the form such as "ID already exists!". The web form is a simple form.html:
<form action="/myapp/rest/customer/created" method="POST">
<table border="1">
<tr>
<td>Customer name:</td>
<td><input type="text" name="name"></td>
</tr>
<tr>
<td>Customer ID:</td>
<td><input type="text" name="id"></td>
</tr>
<tr>
<td>Customer DOB:</td>
<td><input type="text" name="dob"></td>
</tr>
</table>
<br/>
<input type="submit" value="Submit">
</form>
If there is an error occurred, how to pass the error information from Jersey API to the client-side? My server-side POST call associated with this form submission is as follows:
#POST
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
#Path("created")
public Response createCustomer(#FormParam("id") int id,
#FormParam("name") String name, #FormParam("dob") Date dob)
throws ServletException, IOException {
URI uri = URI.create(uriInfo.getPath());
Response r;
r = Response.created(uri).build();
try {
dbController.create(id, name, dob); //This may throw exception.
} catch (DataAccessException ex) {
//To do: How to pass the error message to the client-side UI via e.g., Ajax?
}
return r;
}
First of all add this somewhere in your code. It will display the error message:
<span id="errorDiv" name="errorDiv" class="errorDiv" ></span>
Next, modify your form declaration as:
<form action="/myapp/rest/customer/created" method="POST" onsubmit="return checkForm()">
Before submitting the form it will call checkForm() function. if the function returns true then it will post the form. if not then it will prevent form from submission and display error message.
Assuming that you are submitting the form contents by using jQuery/AJAX calls. You can return a String(default value = 'success') from the server. In case there is an error change the specific string and return it and check the value client-side.
responseTxt is the value returned.
function checkForm(){
//get values from form
var name= $("#name").val();
var id= $("#id").val();
var dob= $("#dob").val();
$.post('DESTINATION',{name:name,id:id,dob:dob},function(responseTxt) {
//MAKE YOUR CHECK HERE. JUST AN EXAMPLE
if (responseTxt.substring(0,4)=='succ'){
//redirect to destination
return true;
}else{
//display error in errorDiv span
$('#errorDiv').html('<font color=red>Wrong username or password.</font>');
//prevents form to be submitted
return false;
}
});
}
Hope it helps
Im getting this error: HTTP Status 405 - Request method 'POST' not supported
What I am trying to do is make a form with a drop down box that get populated based on the other value selected in another drop down box. For example when I select a name in the customerName box the onChange function in the .jsp page should be run and the page submitted then loaded again with the corresponding values in the customerCountry box.
however I'm getting this HTTP Status 405 error. I have searched the internet for a solution but haven't been able to find anything that helped. Here is the relevant parts of my code:
part of jsp page
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<style>
.error { color: red; }
</style>
<script>
function repopulate(){
document.deliveryForm.submit();
}
function setFalse(){
document.getElementById("hasId").value ="false";
document.deliveryForm.submit();
// document.submitForm.submit(); (This was causing the error)
}
</script>
</head>
<body>
<h1>Create New Delivery</h1>
<c:url var="saveUrl" value="/test/delivery/add" />
<form:form modelAttribute="deliveryDtoAttribute" method="POST" action="${saveUrl}" name="deliveryForm">
<table>
<tr>
<td><form:hidden id="hasId" path="hasCustomerName" value="true"/></td>
</tr>
<tr>
<td>Customer Name</td>
<td><form:select path="customerName" onChange="repopulate()">
<form:option value="" label="--- Select ---" />
<form:options items="${customerNameList}" />
</form:select>
</td>
<td><form:errors path="customerName" cssClass="error" /></td>
</tr>
<tr>
<td>Customer Country</td>
<td><form:select path="customerCountry">
<form:option value="" label="--- Select ---" />
<form:options items="${customerCountryList}" />
</form:select>
</td>
<td><form:errors path="customerCountry" cssClass="error" /></td>
</tr>
</form:form>
<form:form name="submitForm">
<input type="button" value="Save" onClick="setFalse()"/>
</form:form>
</body>
</html>
part of controller:
#RequestMapping(value = "/add", method = RequestMethod.GET)
public String getDelivery(ModelMap model) {
DeliveryDto deliveryDto = new DeliveryDto();
model.addAttribute("deliveryDtoAttribute", deliveryDto);
model.addAttribute("customerNameList",
customerService.listAllCustomerNames());
model.addAttribute("customerCountryList", customerService
.listAllCustomerCountries(deliveryDto.getCustomerName()));
return "new-delivery";
}
// I want to enter this method if hasId=true which means that a value in the CustomerName
// drop down list was selected. This should set the CountryList to the corresponding values
// from the database. I want this post method to be triggered by the onChange in the jsp page
#RequestMapping(value = "/add", method = RequestMethod.POST, params="hasCustomerName=true")
public String postDelivery(
#ModelAttribute("deliveryDtoAttribute") DeliveryDto deliveryDto,
BindingResult result, ModelMap model) {
model.addAttribute("deliveryDtoAttribute", deliveryDto);
model.addAttribute("customerNameList",
customerService.listAllCustomerNames());
model.addAttribute("customerCountryList", customerService
.listAllCustomerCountries(deliveryDto.getCustomerName()));
return "new-delivery";
}
// This next post method should only be entered if the save button is hit in the jsp page
#RequestMapping(value = "/add", method = RequestMethod.POST, params="hasCustomerName=false")
public String postDelivery2(
#ModelAttribute("deliveryDtoAttribute") #Valid DeliveryDto deliveryDto,
BindingResult result, ModelMap model) {
if (result.hasErrors()) {
model.addAttribute("deliveryDtoAttribute", deliveryDto);
model.addAttribute("customerNameList",
customerService.listAllCustomerNames());
model.addAttribute("customerCountryList", customerService
.listAllCustomerCountries(deliveryDto.getCustomerName()));
return "new-delivery";
} else {
Delivery delivery = new Delivery();
//Setters to set delivery values
return "redirect:/mis/home";
}
}
How come I get this error? Any help would be much appreciated! Thanks
EDIT: Changed hasId to hasCustomerName. I still get the HTTP Status 405 - Request method 'POST' not supported error though.
EDIT2: Commented out the line in the setFalse function that was causing the error
// D
I am not sure if this helps but I had the same problem.
You are using springSecurityFilterChain with CSRF protection. That means you have to send a token when you send a form via POST request. Try to add the next input to your form:
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
Check if you are returning a #ResponseBody or a #ResponseStatus
I had a similar problem. My Controller looked like that:
#RequestMapping(value="/user", method = RequestMethod.POST)
public String updateUser(#RequestBody User user){
return userService.updateUser(user).getId();
}
When calling with a POST request I always got the following error:
HTTP Status 405 - Request method 'POST' not supported
After a while I figured out that the method was actually called, but because there is no #ResponseBody and no #ResponseStatus Spring MVC raises the error.
To fix this simply add a #ResponseBody
#RequestMapping(value="/user", method = RequestMethod.POST)
public #ResponseBody String updateUser(#RequestBody User user){
return userService.updateUser(user).getId();
}
or a #ResponseStatus to your method.
#RequestMapping(value="/user", method = RequestMethod.POST)
#ResponseStatus(value=HttpStatus.OK)
public String updateUser(#RequestBody User user){
return userService.updateUser(user).getId();
}
You might need to change the line
#RequestMapping(value = "/add", method = RequestMethod.GET)
to
#RequestMapping(value = "/add", method = {RequestMethod.GET,RequestMethod.POST})
The problem is that your controller expect a parameter hasId=false or hasId=true, but you are not passing that. Your hidden field has the id hasId but is passed as hasCustomerName, so no mapping matches.
Either change the path of the hidden field to hasId or the mapping parameter to expect hasCustomerName=true or hasCustomerName=false.
I found the problem that was causing the HTTP error.
In the setFalse() function that is triggered by the Save button my code was trying to submit the form that contained the button.
function setFalse(){
document.getElementById("hasId").value ="false";
document.deliveryForm.submit();
document.submitForm.submit();
when I remove the document.submitForm.submit(); it works:
function setFalse(){
document.getElementById("hasId").value ="false";
document.deliveryForm.submit()
#Roger Lindsjö Thank you for spotting my error where I wasn't passing on the right parameter!
I was getting similar problem for other reason (url pattern test-response not added in csrf token)
I resolved it by allowing my URL pattern in following property in config/local.properties:
csrf.allowed.url.patterns = /[^/]+(/[^?])+(sop-response)$,/[^/]+(/[^?])+(merchant_callback)$,/[^/]+(/[^?])+(hop-response)$
modified to
csrf.allowed.url.patterns = /[^/]+(/[^?])+(sop-response)$,/[^/]+(/[^?])+(merchant_callback)$,/[^/]+(/[^?])+(hop-response)$,/[^/]+(/[^?])+(test-response)$
In my case the url was ending with /
paymentUrl(old)= /get-details/
i just removed the trailing /
paymentUrl(new)= /get-details
and it worked