How do I show error information in web form? - java

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

Related

What is the difference between View and Redirection in Thymeleaf?

I understand that this question might sound stupid to a few but I am new to Spring Boot and Thymeleaf.
Suppose I am on page1.html currently. The user clicks on some button which generates a POST/GET request to my controller MyController.java that maps this request to some function using #RequestMapping.
After I am done processing the request I return a view name like return "page2" from my controller.
Contents of the page1.html change but the url still remains the same like http://localhost:8080/page1 instead of changing to http://localhost:8080/page2
I know that in order to go to a different page I have to use redirection but why is this not possible with view?
Could someone explain me why does this happen and when should I use redirection or when should I use view?
EDIT 1:
Here is my controller
#RequestMapping (value = "edit", method = RequestMethod.POST, params="action=ADD")
{
public String saveUser(#ModelAttribute UserDto userDto) {
try {
User user = new User();
if(userDto.getId() != null) {
throw new UserExists();
}
user.setName(userDto.getName());
user.setEmail(userDto.getEmail());
System.err.println("Request Recieved");
userDao.save(user);
return "success";
} catch (Exception e) {
System.err.println("Error Saving User");
e.printStackTrace();
return "failure";
}
}
Here is my view
<form id="manipulate-data-form" th:object="${userobj}" method="post" th:action="#{/edit}">
<table id="manipulate" class="table table-bordered">
<tr>
<td>ID</td>
<td><input type="text" class="form-control" th:if="${userobj != null}" th:field="*{id}" ></input></td>
</tr>
<tr>
<td>NAME</td>
<td><input type="text" class="form-control" th:if="${userobj != null}" th:field="*{name}" ></input></td>
</tr>
<tr>
<td>EMAIL</td>
<td><input type="text" class="form-control" th:if="${userobj != null}" th:field="*{email}" ></input></td>
</tr>
<tr>
<td><input name="action" value="ADD" type="submit" class="btn btn-success" ></input></td>
<td>
<input name="action" value="DELETE" type="submit" class="btn btn-danger" ></input>
<input style="margin-left: 30px" name="action" value="UPDATE" type="submit" class="btn btn-primary" ></input>
</td>
</tr>
</table>
</form>
So basically clicking the button it invokes the saveUser() Controller and then when I return the failure or success view the URL remains the same but the contents of the the page change corresponding to success or failure.
Let me try to answer some of your questions:
The contents of the page1.html change but the url still remains the
same like http://localhost:8080/page1 instead of changing to
http://localhost:8080/page2
You are hitting an URL like http://localhost:8080/page1. /page1 does not mean to show your view with the name page1.html. Its more the Url for your #RequesteMapping annotation and which method should be executed for this request.
For example:
Your are hitting the Url:
http://localhost:8080/page1 and your mapping is: #RequestMapping("/page1") and you want return page1.html. So your view is: return "page1";
Now you see the content of your page1.html. Your provided button should now have a link to http://localhost:8080/page2 and your Controller should have a mapping to it #RequestMapping("/page2"). In this method you can return your return "page2" and your Url will change to http://localhost:8080/page2.
And if the link of the button does have another value instead of http://localhost:8080/page2 but you want to show this particular URL of /page2 you need a redirection: return "redirect:/page2" in the method of the particular #RequestMapping("/anyOtherMapping").
Could someone explain me why does this happen and when should I use redirection or when should I use view?
I am not 100% sure if this is correct but I think its just a rule why there is view and redirection. If you want to stand on the particular URL and want to show the view which was build for that, just do it. If you dont want to show the same URL or want to switch to another page, just use redirection.
A redirection also calls a controller again which is correct mapped to your redirections URL and return a view.
Some examples for using redirection are the follows:
after a successful login you can redirect to an users home page.
after saving an item (REST approach) you can redirect to the specific id for this item /item/1 and return a view which includes informations of that item.
Answer for your edit question:
Try to use redirection for changing url.
#RequestMapping (value = "edit", method = RequestMethod.POST, params="action=ADD")
{
public String saveUser(#ModelAttribute UserDto userDto) {
try {
User user = new User();
if(userDto.getId() != null) {
throw new UserExists();
}
user.setName(userDto.getName());
user.setEmail(userDto.getEmail());
System.err.println("Request Recieved");
userDao.save(user);
return "redirect:/success";
} catch (Exception e) {
System.err.println("Error Saving User");
e.printStackTrace();
return "redirect:/failure";
}
}
#RequestMapping("/success")
public String successPage(){
return "success";
}
#RequestMapping("/failure")
public String failurePage(){
return "failure";
}
This should change the URL because you will make a redirection from your /edit to /failure or /success page. You can also give parameters within your redirection if you want.

OnClick Event Not Firing Before Spring MVC Controller

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.

`null` result from request in servlets

I am new in servlets-
I am filling text in form but value in request is null-
In login page-
<body>
<form action="">
<input type="text" name="uname">
<input type="text" name="pwd">
link
</form>
</body>
In DisplayPage-
<body>
Display:
<%
String uname=(String)request.getParameter("uname");
String upass=(String)request.getParameter("pwd");
out.println(uname+" - "+upass);
Enumeration<String> enumeration = request.getParameterNames();
boolean b=enumeration.hasMoreElements();
out.println(b);
while (enumeration.hasMoreElements()) {
String name = (String) enumeration.nextElement();
String data=(String)request.getParameter(name);
out.println(name+" - "+data);
}
%>
</body>
Now in my result value of uname and upass is null and hence boolean b is false.Weird!
My Question Is- If request object is created when we use anchor tag since there is no NPE on calling getParameter() on request object,so what kind of data attached with this request object.why this is provided to us?
Because you are not submitting your form to server or not passing any value in url, instead you are clicking on link, which will redirect it to your link.
<body>
<form action="display.jsp"> // added action
<input type="text" name="uname">
<input type="text" name="pwd">
<button type="submit">Link</button> // added submit button
</form>
</body>
For Updated Question
On server side every request is handled as HttpServletRequest object. So when we submit the form, every input field is submitted and then it is retrieved from the request object on server side.

Passing parameters from JSP to Controller in URL issue

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

How to pass a session value as request parameter from JSP to servlet?

<c:forEach var="it" items="${sessionScope.projDetails}">
<tr>
<td>${it.pname}</td>
<td>${it.pID}</td>
<td>${it.fdate}</td>
<td>${it.tdate}</td>
<td> Related Documents</td>
<td>${it.pdesc}</td>
<form name="myForm" action="showProj">
<td><input id="button" type="submit" name="${it.pID}" value="View Team">
</td>
</form>
</c:forEach>
Referring to the above code, I am getting session object projDetails from some servlet, and displaying its contents in a JSP. Since arraylist projDetails have more than one records the field pID also assumes different value, and the display will be a table with many rows.
Now I want to call a servlet showProj when the user clicks on "View Team" (which will be in each row) based on the that row's "pID".
Could someone please let me know how to pass the particular pID which the user clicks on JSP to servlet?
Instead of an <input> for each different pID, you could use links to pass the pID as a query string to the servlet, something like:
View Team
In the showProj servlet code, you'll access the query string via the request object inside a doGet method, something like:
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
String pID = request.getParameter("pID");
//more code...
}
Here are some references for Java servlets:
HttpServletRequest object
Servlet tutorials
Pass the pID along in a hidden input field.
<td>
<form action="showProj">
<input type="hidden" name="pID" value="${it.pID}">
<input type="submit" value="View Team">
</form>
</td>
(please note that I rearranged the <form> with the <td> to make it valid HTML and I also removed the id from the button as it's invalid in HTML to have multiple elements with the same id)
This way you can get it in the servlet as follows:
String pID = request.getParameter("pID");
// ...
Define onclick function on the button and pass the parameter
<form name="myForm" action="showProj">
<input type='hidden' id='pId' name='pId'>
<td><input id="button" type="submit" name="${it.pID}" value="View Team" onclick="populatePid(this.name)">
</td>
.....
Define javascript function:
function populatePid(name) {
document.getElementById('pId') = name;
}
and in the servlet:
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
String pID = request.getParameter("pId");
.......
}

Categories