Why does the <c:if> statement doesn't execute in the jsp? [duplicate] - html

This question already has answers here:
Can not find the tag library descriptor for “http://java.sun.com/jsp/jstl/core” [duplicate]
(18 answers)
Closed 7 years ago.
This is a Spring Web MVC project where I do input validation in server side. If there are any errors, then I add it to the model before sending it to the view.
Controller
#Controller("resultController")
public class ResultController {
private final ResultService resultService;
#Autowired
public ResultController(ResultService resultService) {
this.resultService = resultService;
}
// #RequestMapping(value = "/search", method = RequestMethod.GET)
#RequestMapping(value ="/template", method = RequestMethod.GET)
public String getPersonList(ModelMap model) {
System.out.println("We are coming into this place");
return "header";
}
#RequestMapping(value = "/number", method = RequestMethod.POST, params = { "regNo" })
public String getStudentResult(#RequestParam(value = "regNo", required = true) String regNo, ModelMap model){
//Server side validation
if(regNo.equals(null) || regNo.isEmpty()){
model.addAttribute("nullValue", "Register Number field cannot be empty");
return "header";
}else if(regNo.length() != 12 ){
System.out.println("This Sys out is shown");
model.addAttribute("invalidLength", new String("invalid"));
return "header";
}else{
model.addAttribute("studentResult",resultService.getStudentResult(regNo));
return "numberResult";
}
}
}
header.jsp
<%# page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<head>
<script src="http://code.jquery.com/jquery.min.js"></script>
<style>
#mycontainer, h1, h3 {
text-align:center;
}
form {
display:inline-block;
}
/* #regNoErrorMsgNumber {
display: none;
background: brown;
color: white;
} */
</style>
</head>
<body>
<div id="mycontainer">
<form method="post" action="number" id="number">
<!-- <div id="regNoErrorMsgNumber">Only numbers are allowed</div> -->
<div style="text-align: center;" >
<!-- //TODO: Only number, no spaces, no special symbol and 12 digit check-->
<input width="20" type="text" data-validation="numbers" id="regNo" name="regNo" size="30" maxLength="50" placeholder="Enter Register Number"> <b>OR</b>
<div>
<c:if test="${not empty nullValue}">
<c:out value="${nullValue}"/>
</c:if>
<c:if test="${not empty invalidLength}">
<c:out value="Register Number should be 12 digits"/>
</c:if>
</div>
</div>
</form>
<form method="post" action="name" id="name">
<input type="text" id="studentName" name="studentName" size="30" maxLength="50" placeholder="Enter Student Name"></input>
</form>
</div>
<div style="text-align: center;">
<input id="inputFields" type="button" value="Search" />
</div>
<!-- </form> -->
<script>
$(document).ready(function(){
$('#inputFields').click(function(event){
if (document.getElementById('regNo').value !=""){
$("#number").submit();
}else if(document.getElementById('studentName').value !=""){
$("#name").submit();
}
});
});
</script>
</body>
The following piece of jstl code in jsp doesn't work
<c:if test="${not empty invalidLength}">
<c:out value="Register Number should be 12 digits"/>
</c:if>
Also if I use the c:out statement without c:if tag, then it works. But it misaligns two input fields in UI. You can see the div mycontainer code in jsp. I want the error message to be shown below the regNo input field, but at the same time regNo and studetnName input field should be center aligned in a single line.
PS: I get Can not find the tag library descriptor for "http://java.sun.com/jsp/jstl/core". Try increasing the version of the Dynamic Web Module project facet, as this method of reference may not be supported by the current JSP version (1.1)., but c:out tag with being wrapped with c:if works.

please try the following :
if you are using maven , add this to your dependencies and maven will add the jar for you :
<dependencies>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
if you are not using maven add the jstl library to your project (jstl-1.2.jar)
make sure you set a Targeted Runtime for your project , Tomcat , Glassfish , etc ...
and please refer to this question here .
for the errors part , use the <form:errors> from spring form tags :
- first add the following to your page :
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
then use the form:errors like the following :
<form:errors path="userName" cssClass="error" element="div" />
please refer to the following tutorials , here and here .
Hope that helps

I created a new class called ProjectErrors and I changed the code as follows
if(regNo.equals(null) || regNo.isEmpty()){
univErrors.setHasError(true);
model.addAttribute("isNull", univErrors );
}else if(Pattern.matches("[a-zA-Z]+", regNo)){
univErrors.setHasError(true);
model.addAttribute("onlyNumbers", univErrors );
}else if(regNo.length() != 12 ){
univErrors.setHasError(true);
model.addAttribute("length", univErrors );
}
I changed the jsp like this
<c:if test="${length.hasError}">
<c:out value="Register Number should be 12 digits."/>
</c:if>
<c:if test="${onlyNumbers.hasError}">
<c:out value="Register number can contain only digits."/>
</c:if>
And my error class looks like this
public class ProjectErrors {
public Boolean hasError;
public ProjectErrors(boolean b) {
// TODO Auto-generated constructor stub
hasError = b;
}
public Boolean getHasError() {
return hasError;
}
public void setHasError(Boolean hasError) {
this.hasError = hasError;
}
}
Now I see c:if tag working.
But, still get the warning in jsp "Can not find the tag library descriptor for "http://java.sun.com/jsp/jstl/core". Try increasing the version of the Dynamic Web Module project facet, as this method of reference may not be supported by the current JSP version (1.1).",

Related

How do i pass two objects from thymeleaf form to Spring Controller?

I want to pass two objects from thymeleaf form to a controller. Here is my thymeleaf code :
<!DOCTYPE html>
<html lang='en' xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1"/>
<title>Payment Page</title>
</head>
<body>
<h1>Payment List</h1>
<table>
<tr>
<th>StuId</th>
<th>Month</th>
<th>Amount</th>
</tr>
<tr th:each="payment:${student.payments}">
<td th:text="${student.id}">2</td>
<td th:text="${payment.month}">devesh</td>
<td th:text="${payment.amount}">23</td>
</tr>
</table>
<h3>Add a Payment</h3>
<form action="#" th:action= "#{/payments}" th:object="${payment}" method="POST">
<div th:object="${student}" >
<label for="name">Month:</label>
<input type="text" name="month" size="50"></input><br/>
<label for="amount">Amount:</label>
<input type="text" name="amount" size="50"></input><br/>
<input type = "submit"/></form>
</body>
</html>
in form except the payment object which is actually being submitted here , i want to pass student object or id to my controller as any payment should correspond to a particular student. i Could not find any way till now after searching a lot.
PaymentController method where i want to pass the objects , as i am using submit form , i could not pass variable in th:action
#RequestMapping(method = RequestMethod.POST, value = "/{id}/payments")
public String doPayment(#ModelAttribute("payment") PaymentRecord paymentRecord, #PathVariable int id) {
Student st = studentService.getStudentInfo(id);
st.addPayments(paymentRecord);
System.out.println("entered into do payment");
studentService.addStudent(st);
paymentService.doPayment(paymentRecord);
return "redirect:{id}/payments";
}
Please suggest. I am stuck here
You can do this different ways.
But first, let's shorten the post method annotation from:
#RequestMapping(method = RequestMethod.POST, value = "/{id}/payments")
to
#PostMapping("/{id}/payments")
I would also change the variable id to studentId to make it more clear for the next person reading your code.
Now, include the variable as a hidden input type within the form tags:
<input type="hidden" name="studentId" th:value="${student.id}">
Then you can either
a) Add a String studentId property to your PaymentRecord class. Then in the post method, you can call paymentRecord.getStudentId();
or
b) Add a request parameter for the studentId to your method mapped with #PostMapping. So your method signature can have #RequestParam(String studentId). In this case, the value of the studentId will be fully exposed to the user.
or
c) Use #RequestBody and map the values to a bean. You can read up on this topic further and look at some background with this question.
Instead of :
return "redirect:{id}/payments";
try this:
return "redirect:" + id + "/payments";
or (if need leading / in the redirect) :
return "redirect:/" + id + "/payments";
Of if you want to "GET" with multiple objs then you can something like:
#RequestMapping(value = { "/deleteuserdocument/{docid}/{userid}" }, method = RequestMethod.GET)
This is from my github repo where I am "GET"ting the documentid for a specific userid. Hope this might help.

How to create single page search form with ajax in MVC Razor

I'm a beginner in the ways of MVC / Razor... Basically I have 2 pages just for showing a search result from an API that I'm consuming with Json.
Index.cshtml: (Just a form)
#model ConsultaInterna.Models.SearchApi
<div class="jumbotron">
<h2>Consulta Interna</h2>
Placa
#using (Html.BeginForm("Search", "Home"))
{
<input asp-for="Model.Name" class="form-control" name="name" id="txtName" />
<input class="form-control" type="submit" id="ok" />
}
and Search.cshtml (Which also have the form. I want to leave just this page, right now I can't because if I load directly from Search.cshtml it gives me an error like
"Unexpected character encountered while parsing value: <. Path '', line 0, position 0. " (Obviously because I'm trying to load labels with a null search)
this is Search.cshtml
#model ConsultaInterna.Models.SearchApi
<div id="loader" class="spinner" style="display:none">OIE</div>
<div class="header formPesquisa">
#using (Ajax.BeginForm("Search", new AjaxOptions()
{
HttpMethod = "POST",
LoadingElementId = "loader",
UpdateTargetId = "resultado"
}))
{
<input placeholder="Placa" asp-for="Model.Name" name="name" id="txtName" width="30" />
<input class="pesquisa" type="submit" id="enviar" value="Ok" />
}
<div class="panel">Placa #Model.Name</div>
<div class="panel">Last Name / Model.LastName</div>
ALSO Important, the Ajax form isn't working properly... the loader won't show up.
You need to move your loader div into the Ajax.BeginForm.
You need also need to add the resultado div to display your result.
#using (Ajax.BeginForm("Search", "Home", new AjaxOptions()
{
HttpMethod = "POST",
LoadingElementId = "loader",
UpdateTargetId = "resultado"
}))
{
<div id="loader" class="spinner" style="display:none">OIE</div>
<input placeholder="Placa" asp-for="Model.Placa" class="pesquisa placa" maxlength="7" name="placa" id="txtPlaca" width="30" />
<input placeholder="CPF" asp-for="Model.CPF" class="pesquisa cpf" maxlength="11" name="cpf" id="txtCPF" />
<input class="pesquisa" type="submit" id="enviar" value="Enviar" />
<div id="resultado"></div>
}
<div class="panel">Placa #Model.Name</div>
<div class="panel">Last Name / Model.LastName</div>
</div>
You also need to make sure you have added the relevant reference to the specific unobtrusive ajax library. As an example:
BundleConfig
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.unobtrusive-ajax.min.js",
"~/Scripts/jquery.validate*"));
_layout.cshtml
#Scripts.Render("~/bundles/jqueryval")
Example controller action
[HttpPost]
public ActionResult Search(string placa, string cpf)
{
return new JsonResult { Data = "found", JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}

Botdetect Captcha HTML <taglib>

I want to add Botdetect Captcha to my html file.But in the website of the Botdetect captcha, there is example only for jsp. In this jsp file, <taglib> is used like that:
<%#taglib prefix="botDetect" uri="https://captcha.com/java/jsp"%>
....
<botDetect:captcha id="basicExample" userInputID="captchaCode" />
<div class="validationDiv">
<input name="captchaCode" type="text" id="captchaCode" value="${basicExample.captchaCode}" />
<input type="submit" name="validateCaptchaButton" value="Validate" id="validateCaptchaButton" />
<span class="correct">${basicExample.captchaCorrect}</span>
<span class="incorrect">${basicExample.captchaIncorrect}</span>
</div>
Is there any alternative for <%#taglib> in HTML files. How can I solve this problem?
I'm facing this problem because I use Thymeleaf instead of JSP so I can't use the taglib but I still have "dynamic HTML". I'm answering assuming this is your case.
I saw a possible solution on the help pages here: https://captcha.com/doc/java/jsp-captcha.html at section 2:
<%
// Adding BotDetect Captcha to the page
Captcha captcha = Captcha.load(request, "exampleCaptcha");
captcha.setUserInputID("captchaCode");
String captchaHtml = captcha.getHtml();
out.write(captchaHtml);
%>
This is JSP code but can be easily adapted to Thymeleaf. This in the #Controller that opens the page:
Captcha captcha = Captcha.load(request, "exampleCaptcha");
captcha.setUserInputID("captchaCode");
String captchaHtml = captcha.getHtml();
model.addAttribute("captchaHtml", captchaHtml);
and the html is like
<th:block th:utext="${captchaHtml}"></th:block>
The code for captcha checking is the same as for JSP, but placed in the #Controller that handles the form:
// validate the Captcha to check we're not dealing with a bot
boolean isHuman = captcha.validate(request.getParameter("captchaCode"));
if (isHuman) {
// TODO: Captcha validation passed, perform protected action
} else {
// TODO: Captcha validation failed, show error message
}
To finish you also need to edit web.xml (if you have it) and import the java libs as by the official docs. I'm using Gradle and importing 'com.captcha:botdetect-servlet:4.0.beta3.7'
Warning: your server should be on HTTPS or you might get this error when using version 4.0.beta3.7:
Captcha.UserInputID is not set. Your implementation of BotDetect is
not completely secure yet
<%#taglib prefix="botDetect" uri="https://captcha.com/java/jsp"%>
....
<botDetect:captcha id="basicExample" userInputID="captchaCode" />
<div class="validationDiv">
<input name="captchaCode" type="text" id="captchaCode" value="${basicExample.captchaCode}" />
<input type="submit" name="validateCaptchaButton" value="Validate" id="validateCaptchaButton" />
<span class="correct">${basicExample.captchaCorrect}</span>
<span class="incorrect">${basicExample.captchaIncorrect}</span>
</div>
That is wrong, with HTML (HyperText Markup Language) you have to list the version of HTML in this case it is Doctype HTML so the following one should be correct...
<!DOCTYPE html>
<%#taglib prefix="botDetect" uri="https://captcha.com/java/jsp"%>
....
<botDetect:captcha id="basicExample" userInputID="captchaCode" />
<div class="validationDiv">
<input name="captchaCode" type="text" id="captchaCode" value="${basicExample.captchaCode}" />
<input type="submit" name="validateCaptchaButton" value="Validate" id="validateCaptchaButton" />
<span class="correct">${basicExample.captchaCorrect}</span>
<span class="incorrect">${basicExample.captchaIncorrect}</span>
</div>

Can I make HTTP POST request from Thymeleaf table in Spring Boot application

I have a Thymeleaf template in a simple Spring Boot application. The template contains a list in a table as follows:
<p>There are <span th:text="${#lists.size(persons)}"></span> people:</p>
<table th:if="${not #lists.isEmpty(persons)}" border="1">
<tr>
<th>ID</th>
<th>Name</th>
<th>Address</th>
<th>Telephone</th>
<th>Email</th>
<th>Actions</th>
</tr>
<tr th:each="person : ${persons}">
<td th:text="${person.personId}"></td>
<td th:text="${person.name}"></td>
<td th:text="${person.address}"></td>
<td th:text="${person.telephone}"></td>
<td th:text="${person.email}"></td>
<td>
Edit |
Delete
</td>
</tr>
</table>
I want to enable edit and delete functionality as per the last cell in the table. But at the moment both requests are for HTTP GET. That is fine for edit where a person's details are fetched from the server for editing, but delete should trigger a POST request because of the data changes on the server.
Does anyone know if Thymeleaf allow a POST request per row of a table? Or do I have to write a simple HTML form per row?
The GET form is currently:
<td>
Edit
<!--a href="#" data-th-href="#{/delete(personId=${person.personId})}">Delete</a></td-->
<form method="get" th:action="#{/edit(personId=${person.personId})}">
<button type="submit" name="submit" value="value">Edit</button>
</form>
</td>
Where I have a link and a form for testing.
The controller method to be called is:
// Gets a Person.
#RequestMapping(value="/edit", method=RequestMethod.GET)
public String getEditPerson(#RequestParam("personId") String personId, Model model) {
logger.info(PersonController.class.getName() + ".getEditPerson() method called.");
Person person = personDAO.get(Integer.parseInt(personId));
model.addAttribute("person", person);
// Set view.
return "/edit";
}
The error when the button version of GET is called is:
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Sun Jul 24 00:26:16 BST 2016
There was an unexpected error (type=Bad Request, status=400).
Required String parameter 'personId' is not present`
I am using GET to trigger editing because no data is sent to the server here other than the personId. No database action is taken so it should be a GET.
you are using Links and I don't think that is possible, you would need to use a form where you can specify the method POST to be used.
In the example below im using a <button> instead of a <a> element, but it will work, the only thing you need to do is to style your button with CSS to look like your links
<form method="POST" th:action="#{/edit(personId=${person.personId})}">
<button type="submit" name="submit" value="value" class="link-button">This is a link that sends a POST request</button>
</form>
now in your code should look like this
<tr th:each="person : ${persons}">
<td th:text="${person.personId}"></td>
<td th:text="${person.name}"></td>
<td th:text="${person.address}"></td>
<td th:text="${person.telephone}"></td>
<td th:text="${person.email}"></td>
<td>
<form method="POST" th:action="#{/edit(personId=${person.personId})}">
<button type="submit" name="submit" value="value" class="link-button">EDIT</button>
</form> |
<form method="POST" th:action="#{/delete(personId=${person.personId})}">
<button type="submit" name="submit" value="value" class="link-button">DELETE</button>
</form>
</td>
</tr>
EDIT
As you just shared you Java code, in the controller you are expecting the personId not as a PathVariable, but as a RequestParam,
in that case your form should have that value...
edit your form and add the person id as follows.
<form method="POST" th:action="#{/edit}">
<input type="hidden" name="personid" id="personId" th:value="${person.personId}" />
<button type="submit" name="submit" value="value" class="link-button">This is a link that sends a POST request</button>
</form>
Notice also I changed the action of the form to be just /edit, as its what your controller looks like
Does anyone know if Thymeleaf allow a POST request per row of a table? Or do I have to write a simple HTML form per row?
HTML doesn't support POST request with links and you have to use forms (as Rayweb_on explained). But Thymeleaf allows you to define custom tags which helps a lot :
<a th:href="#{/edit(personId=${person.personId})}" custom:linkMethod="post">Edit</a>
... which would generate following HTML (assuming jQuery is available) :
Edit
Custom tag definition (without error checking to keep it simple) :
/**
* Custom attribute processor that allows to specify which method (get or post) is used on a standard link.
*/
public class LinkMethodAttrProcessor extends AbstractAttributeTagProcessor {
private static final String ATTR_NAME = "linkMethod";
private static final int PRECEDENCE = 10000;
public LinkMethodAttrProcessor(final String dialectPrefix) {
super(
TemplateMode.HTML, // This processor will apply only to HTML mode
dialectPrefix, // Prefix to be applied to name for matching
null, // No tag name: match any tag name
false, // No prefix to be applied to tag name
ATTR_NAME, // Name of the attribute that will be matched
true, // Apply dialect prefix to attribute name
PRECEDENCE, // Precedence (inside dialect's own precedence)
true); // Remove the matched attribute afterwards
}
#Override
protected void doProcess(final ITemplateContext context, final IProcessableElementTag tag,
final AttributeName attributeName, final String attributeValue,
final IElementTagStructureHandler structureHandler) {
// get the method name (tag parameter)
final IEngineConfiguration configuration = context.getConfiguration();
final IStandardExpressionParser parser = StandardExpressions.getExpressionParser(configuration);
final IStandardExpression expression = parser.parseExpression(context, attributeValue);
final String method = (String) expression.execute(context);
// add custom javascript to change link method
final String link = tag.getAttribute("href").getValue();
final String action = "$('<form action="" + link + "" method="" + method + ""></form>').appendTo('body').submit(); return false;";
structureHandler.setAttribute("onclick", action);
structureHandler.setAttribute("href", "#");
}
}
See the Thymelead documentation for example of how this custom attribute needs to be registered.

How to Get Model Data from Partial View?

I am creating a site in which I utilize partial views to display various bits of data about a single Model. Here is a bit of the HTML. (Note, all of these are contained within a single form and the Index page that these partials are rendered in is strongly typed to the main model. The main model contains various lists of data.)
<div id="tab1"><% Html.RenderPartial("Tab1", Model); %></div>
<div id="tab2"><% Html.RenderPartial("Tab2", Model.AnItemList1.FirstOrDefault<AnItemList1>()); %></div>
<div id="tab3"><% Html.RenderPartial("Tab3", Model.AnItemList2.FirstOrDefault()); %></div>
Here is ONE of the partial views headers (for 'tab2'):
<%# Language="C#" Inherits="System.Web.Mvc.ViewUserControl<AnItem1>" %>
The pages display correctly. The issue is that, when I enter data into the various parts of the partial pages and then submit the entire form (via POST), the data is not making it back to my data store (MSSQL) - but this only happens for any of the list items (that are contained within the Model). The first partial page does properly have its data set within the data store.
What am I doing wrong here? Should I only be passing the model to Html.RenderPartial and then get the specific model I need on the partial page? Should I pass the entire list and then get the first (right now, I only care about the first item in the list - that will EVENTUALLY change, but not any time soon).
Suggestions or thoughts appreciated.
Update: Here is how I accessing the properties on the partial views.
<div class="data-group">
<%: Html.CheckBoxFor(model => model.Property1) %>
<%: Html.LabelFor(model => model.Property1) %>
</div>
Update 2: Per request...
Controller Action (ScenarioController):
public ActionResult Index(int id = 0)
{
if (id == 0)
{
SavedScenario scenario = new SavedScenario();
scenario.AnItemList1.Add(new AnItem1());
scenario.AnItemList2.Add(new AnItem2());
return View("Index", scenario);
}
else
{
SavedScenario scenario = repository.GetScenario(id);
if (scenario == null)
return View("NotFound");
else
return View("Index", scenario);
}
}
[HttpPost]
public ActionResult Index(SavedScenario scenario)
{
if (ModelState.IsValid && TryUpdateModel(scenario, "SaveScenario"))
{
repository.Add(scenario);
repository.Save();
}
return View(scenario);
}
Rendered HTML (I can only include parts of it - this is a small sample of what is in the form):
<form action="/Scenario" id="form0" method="post">
<!-- This is the one that works - the basic Scenario. Top level. -->
<fieldset>
<legend>Scenario Information</legend>
<div class="data-group">
<div class="editor-label">
<label for="ScenarioName">Scenario Name</label>
</div>
<div class="option1">
<input class="wide" id="ScenarioName" name="ScenarioName" type="text" value="" />
</div>
<div class="validation">
<div><span class="field-validation-valid" id="ScenarioName_validationMessage"></span></div>
</div>
</div>
</fieldset>
<!-- This does not work or get submitted (as far as I can tell). -->
<div id="tab2">
<fieldset>
<legend>Tab2</legend>
<div class="data-group">
<input id="Property1" name="Property1" type="checkbox" value="true" /><input name="Property1" type="hidden" value="false" />
<label for="Property1" />
</div>
</div>
</fieldset>
</form>
My apologies for having to keep this so generic.
Hard to guess from this much code. However you should make sure that all properties of your models have the same prefix when they are posted back to the server
Edit: form field names should match property names of your model to correctly bind all values. You have two fields with the same name that you can bind in following way
[HttpPost]
public ActionResult Index(SavedScenario scenario, List<bool> Property1)
{
// here you can do with values coming in property1
if (ModelState.IsValid && TryUpdateModel(scenario, "SaveScenario"))
{
repository.Add(scenario);
repository.Save();
}
return View(scenario);
}
It might be issue with naming the fields on your partial forms. Try naming the fields on your partial views by prefixing it with the name of the Model passed into it...like 'AnItemList1.name' instead of just 'name'..I am just guessing here though...but that's what I did sometimes to fix the problem when I was getting values as null..