I have been trying this for a while but can't find the right solution.
I want to use JSTL to check if there is any binding errors (field error or global error) that happened in my Spring MVC 2.5.
I know I can use this code:
<p>
<spring:hasBindErrors name="searchItems">
An Error has occured
</spring:hasBindErrors>
</p>
But I want to utilize JSTL to check for any errors.
I have tried this one using JSTL:
<c:if test="${not empty errors}">
An Error has occured
</c:if>
But it seems that I cannot catch it correctly.
I need to used JSTL since there are other parts of the JSP that relies on the presence or absence of a binding errors.
As said
I want to utilize JSTL to check for any errors
Just use (It just works on Spring MVC 2.5 - Not portable for Spring MVC 3.0 although i suppose it is requestScope['bindingResult.<COMMAND_NAME_GOES_HERE>.allErrors'])
<c:if test="${not empty requestScope['org.springframework.validation.BindingResult.<COMMAND_NAME_GOES_HERE>'].allErrors}">
An Error has occured!!!
</c:if>
Keep in mind default command name is The non-qualified command class name with The first letter lowercased. Notice bellow command name is pet
private PetValidator petValidator = new PetValidator();
#RequestMapping(method.RequestMethod.POST)
public void form(Pet command, BindingResult bindingResult) {
if(petValidator.validate(command, bindingResult)) {
// something goes wrong
} else {
// ok, go ahead
}
}
So your form should looks like
<!--Spring MVC 3.0 form Taglib-->
<form:form modelAttribute="pet">
</form:form>
<!--Spring MVC 2.5 form Taglib-->
<form:form commandName="pet">
</form:form>
Unless you use #ModelAttribute
#RequestMapping(method.RequestMethod.POST)
public void form(#ModelAttribute("command") Pet command, BindingResult bindingResult) {
// same approach shown above
}
This way, your form should looks like
<!--Spring MVC 3.0 form Taglib-->
<form:form modelAttribute="command">
</form:form>
<!--Spring MVC 2.5 form Taglib-->
<form:form commandName="command">
</form:form>
Something like this:
<spring:hasBindErrors name="userName">
<c:set var="userNameHasError" value="true" />
</spring:hasBindErrors>
<c:choose>
<c:when test="${userNameHasError}">
<%-- Display username as textbox --%>
</c:when>
<c:otherwise>
<%-- Display username as label --%>
</c:otherwise>
</c:choose>
You can probably also put a setup the errors to catch all errors on the page (untested):
<spring:hasBindErrors name="*">
<c:set var="userNameHasError" value="true" />
</spring:hasBindErrors>
Enjoy!
After playing around with <spring:hasBindErrors> tag, I found it had certain limitations:
It is useful only when there are errors.
org.springframework.validation.Errors object is only accessible inside the tag
What if just wanted to know if there are errors or not. If there are no errors, <spring:hasBindErrors> is rendered useless. After doing some research with my colleague, we printed out all the request attributes. Turns out there is an attribute called:
org.springframework.validation.BindingResult.command
The command object here is your form backing command object.
As unintuitive as it may be named, this attribute holds a reference to our Errors object.
Thus, this works:
${requestScope['org.springframework.validation.BindingResult.command'].errorCount}
and gives us a handle over the much sought after Errors object in JSTL
Related
I am listing objects in a table in my view. I want to be able to edit an object using a button in the table.
<#list products as product>
<tr>
<td>${product.productName}</td>
<td>${product.price}</td>
<td>${product.quantity}</td>
<td>
<form name="product" method="post" action="/product/edit">
<input type="submit" name="submit" value="Edit this product"/>
</form>
</td>
</tr>
</#list>
The object then should be passed to a controller method:
#RequestMapping(value="/edit", method = RequestMethod.POST)
public ModelAndView edit(#ModelAttribute("product") Product product){
ModelAndView mav = new ModelAndView("product/edit");
mav.addObject("product", product);
return mav;
}
However, the product obtained by the edit method is null. How do I fix this? I tried to bind the product inside form using the code below, but that did not work either.
<form name="product" method="post" action="/product/edit">
<#spring.bind "product" />
<input type="hidden" name="${spring.status.expression}" value="${spring.status.value}"/>
<input type="submit" name="submit" value="Edit this product"/>
</form>
I want to use the POST method.
I would like to suggest a different approach. If I'm not mistaken you just want to pick an object for later editing - you don't really edit it in that very view.
If so, all you have to do is to pass an identifier of your object to your controller, but not the selected object itself.
If not, you should give us the hole story and provide the rest of the view as well.
Assuming I'm right the next question is why you need to use a form submission at all. Passing an id is best done by links - either as parameter or, if you follow REST-style, as part of the URI itself:
<!-- Link parameter -->
<#list products as product>
<tr>
<td>${product.productName}</td>
<td>${product.price}</td>
<td>${product.quantity}</td>
<td>
Edit ${product.productName}
</td>
</tr>
</#list>
<!-- REST-style -->
...
Edit ${product.productName}
...
productName isn't a good id of course. If products is a list (meaning, java.util.List) the index of the list is handy. Even in a HashMap or Set I'd create a unique id instead of using the product name.
Now that you can identify your object, select it in the backing code for later editing, but not in the view.
You'll find loads of examples of how to get link parameters in a controller. So, no need to go into detail here.
If however you insist on using a form and a POST-method then do it like this:
<form method="post" action="/product/edit">
<#list products as product>
<tr>
<td>${product.productName}</td>
<td>${product.price}</td>
<td>${product.quantity}</td>
<td>
<button value="${product.productName}" name="product" type="submit">Edit ${product.productName}</button>
</td>
</tr>
</#list>
</form>
Note that this won't work for older IE browsers (below Ver. 10), because they don't return the value, but everything that is inside the button tag.
Hidden inputs and a single submit button won't help at all, because all inputs are submitted and using different forms is not the way either.
I have a table, using display tag, in my application, that is using spring web flow. I would like to have a check box in each row, a button that allows me to select/uselect all and a button to execute a function. After clicking the button, the action will perform some database actions and the page should be render, so we can see these changes.
I don´t know which could be the best option, submitting the whole table
<form method="POST" (more params)>
<display:table id="row">
....
</display:table>
</form>
Or only the checkbox column. I this case I wouldn´t know how to implement it.
I have tryed two different approaches:
1. Using a simple input text, checkbox type. This is not possible, because when I submit the form, I need to set a path to another page.jsp (I am working with flows). Besides, I wouldn´t know how to send these values to java backend.
Using spring tags.
In this case, the problem comes whith the class conversationAction
I found some examples, but allways using MVC and controller cases.
How could I implement this issue??
EDIT
I have found a kind of solution, but I faced a new problem...
flow.xml
var name="model1" class="com.project.Model1"/>
var name="model2" class="com.project.Model2"/>
view-state id="overview" model="formAggregation">
...
</view-state>
page.jsp
form:form modelAttribute="formAggregation.model1" id="overviewForm">
...
/form:form>
...
form:form method="POST" modelAttribute="formAggregation.model2">
display:table id="row" name="displayTagValueList" requestURI="overview?_eventId=tableAction">
display:column title="">
form:checkbox path="conversationIds" value="${row.threadId}"/>
/display:column>
/display:table>
input type="submit" name="_eventId_oneFunction" value="Send>>"/>
/form:form>
FormAggregation.java
#Component("formAggregation")
public class FormAggregation {
private Model1 model1;
private Model2 model2;
//Getters and setters
I need this aggregator, because I need both models. I have tested it one by one and it is working as wished. Any idea about that??
Thanks!!
I couldn´t find a solution to add two model in a view-state. So I made a workaround, adding the fields I needed to the model I was using, com.project.Model1. So the result is:
page.jsp
<form:form method="POST" id="tableForm" modelAttribute="model1">
<display:table id="row">
<display:column title="">
<form:checkbox path="chosenIds" value="${row.id}"/>
</display:column>
<display:footer>
<div class="tableFooter" >
<input type="submit" name="_eventId_workIds" value="Send"/>
</div>
</display:footer>
</display:table>
</form:form>
flow.xml
<var name="model1" class="com.project.Model1"/>
...
<transition on="workIds" to="overview" validate="false">
<evaluate expression="actionBean.workIds(model1.chosenIds)" />
</transition>
java class
public void workIds(List<Long> ids) {
Hope it helps
I'm using Spring MVC LocaleChangeInterceptor to handle change of locale. I need 2 simple links to switch the current page between English and French while keeping any existing parameter of the current URL.
The solution I've came up with looks rather ugly. A c:url tag with an empty value to link to the current page and a loop to append any existing parameter except for the language parameter that I don't want to see twice. So the code is as follow.
<c:url value="" var="englishURL">
<c:forEach items="${param}" var="currentParam">
<c:if test="${currentParam.key != 'siteLanguage'}">
<c:param name="${currentParam.key}" value="${currentParam.value}"/>
</c:if>
</c:forEach>
<c:param name="siteLanguage" value="en"/>
</c:url>
<c:url value="" var="frenchURL">
<c:forEach items="${param}" var="currentParam">
<c:if test="${currentParam.key != 'siteLanguage'}">
<c:param name="${currentParam.key}" value="${currentParam.value}"/>
</c:if>
</c:forEach>
<c:param name="siteLanguage" value="fr"/>
</c:url>
English / Français
Does someone have a less ugly way to create these links? I know I can create a custom tag to avoid the duplication but I feel that's a very small improvement since I need to declare the tag and all.
Not really less ugly, but atleast some less code duplication
<c:forEach var="tuple" items="${fn:split('en,English|fr,Français|nl,Nederlands', '|')}" varStatus="status">
<c:set var="locale" value="${fn:split(tuple, ',')[0]}"/>
<c:set var="name" value="${fn:split(tuple, ',')[1]}"/>
<c:url value="" var="url">
<c:forEach items="${param}" var="currentParam">
<c:if test="${currentParam.key != 'changeLocale'}">
<c:param name="${currentParam.key}" value="${currentParam.value}"/>
</c:if>
</c:forEach>
<c:param name="changeLocale" value="${locale}"/>
</c:url>
${name}
<c:if test="${not status.last}"> / </c:if>
</c:forEach>
From your question it seems like you are appending the siteLanguage param in your url everytime. Is it correct? Why you need to do this? You can save this language change in session or cookie and then everytime you don't need to specify the siteLanguage parameter. Once the language is selected then the language parameter is stored in the session or cookie. And spring will manage the rest of the things itself.
Just specify either SessionLocaleResolver or CookieLocaleResolver along with your LocaleChangeInterceptor. It will do this for you.
Hope this helps you. Cheers.
I'm using Spring (3.1), Spring MVC (3.1) and Spring Security (3.0) in combination and I've put together a single JSP page that has two forms on it; One is a form to login and the other is a form to register (i.e. create a new user).
For the register form, I use the Spring form tags, backed up by a controller to handle the request but for the login form I don't bother with the Spring form tags as I don't believe they're needed. There is also no controller that I need to write to handle the form submission as Spring Security takes care of authenticating so long as the request is submitted to j_spring_security_check.
The register form is working fine but the login form is a problem. It seems that when I click the submit button on the login form, the registration form is also submitted, or at least Spring thinks I'm trying to submit that form. Here is the JSP:
<form id="loginform" method="POST" action="<c:url value='j_spring_security_check'/>">
<label for="existing_email">Email:</label>
<input name="j_username" id="existing_email" type="text" value="${SPRING_SECURITY_LAST_USERNAME}" />
<label for="existing_password">Password:</label>
<input name="j_password" id="existing_password" type="password" />
<input id="login-form-submit" type="submit" value="Sign in" />
</form>
<form:form id="registrationform" modelAttribute="user" method="POST" action="register">
<form:label path="username" for="email">Email:</form:label>
<form:input path="username" name="username" id="email" type="text" />
<form:errors path="username" cssClass="formError" />
<form:label path="password" for="password">Password:</form:label>
<form:input path="password" name="password" id="password" type="password" />
<form:errors path="password" cssClass="formError" />
<input id="registration-form-submit" type="submit" value="Sign up" />
</form:form>
Notice that form tags for the input of type submit are not present and this seems to be a normal thing to do in the examples I've seen. Adding form tags to the submit button I guess doesn't make sense as it doesn't map to anything on the target object (user in this case).
When I click the "Sign in" button I get the following exception:
SEVERE: Servlet.service() for servlet [appServlet] in context with path [/project1] threw exception [An exception occurred processing JSP page /WEB-INF/views/registration.jsp at line 29
28: <form:form id="registrationform" modelAttribute="user" method="POST" action="register">
29: <form:label path="username" for="username">Username:</form:label>
30: <form:input path="username" name="username" id="username" type="text" />
31: <form:errors path="username" cssClass="formError" />
32:
Stacktrace:] with root cause
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'user' available as request attribute
at org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:141)
This I recognise from cases where you forget to include the modelAttribute attribute in form:form, but of course I don't want to submit this form to my controller.
I have a feeling there is a mistake I'm making or a simple solution. Can anyone recommend a way around this or perhaps a different approach?
Here is the controller method that handles requests to register in case that's needed:
#RequestMapping(value = "**/register", method = RequestMethod.POST)
public String registerUser(#ModelAttribute("user") #Validated User user, BindingResult errors, ModelMap model) {
if (errors.hasErrors()) {
return "registration";
}
// Other stuff then...
return "profile"
}
If you are using "user" modelAttribute in form tag then a non-null request attribute must be present with name "user".
One way to add that in request attribute is what you did in your answer above. Other ways are:
(1) Add in ModelMap:
#RequestMapping(value = "/loginfailed", method = RequestMethod.GET)
public String loginFailed(ModelMap model) {
model.addAttribute("user", new User());
model.addAttribute("error", "true");
return "registration";
}
(2) Add in request scope (Using WebRequest or HttpServletRequest):
#RequestMapping(value = "/loginfailed", method = RequestMethod.GET)
public String loginFailed(ModelMap model, WebRequest webRequest) {
webRequest.setAttribute("user", new User(), WebRequest.SCOPE_REQUEST);
model.addAttribute("error", "true");
return "registration";
}
(3) Use #ModelAttribute on method:
#ModelAttribute("user")
public User user() {
return new User();
}
Please also see Using #ModelAttribute on a method and Using #ModelAttribute on a method argument
Also note that you don't have to use type attribute. See form:input and form:password.
I think the problem is specifically when a login fails and the same page is served up, albeit on a different URL path and so through a different controller method. Therefore my original suspicion that the issue is that both forms are submitted may be something of a red herring, though I still don't fully understand what's going on and that may yet have something to do with it. In any case, this is what corrected the problem for me:
I had a controller method that originally looked like this:
#RequestMapping(value = "/loginfailed", method = RequestMethod.GET)
public String loginFailed(ModelMap model) {
model.addAttribute("error", "true");
return "registration";
}
In the Spring Security context I specify /loginfailed as the path to go to by default if a login attempt fails. This is where it seems the user object is needed so if I alter the signature as follows it all works:
#RequestMapping(value = "/loginfailed", method = RequestMethod.GET)
public String loginFailed(#ModelAttribute("user") User user, BindingResult errors, ModelMap model) {
model.addAttribute("error", "true");
return "registration";
}
Any comments/clarification welcome from others.
<#list flowList as flow>
<#spring.formInput "flow.createDatetime" />
</#list>
flowList is arrayList.
freemarker.template.TemplateModelException: Method public org.springframework.web.servlet.support.BindStatus org.springframework.web.servlet.support.RequestContext.getBindStatus(java.lang.String) throws java.lang.IllegalStateException threw an exception when invoked on org.springframework.web.servlet.support.RequestContext#8bc713e with arguments of types [java.lang.String,]
at freemarker.ext.beans.OverloadedMethodModel.exec(OverloadedMethodModel.java:134)
at freemarker.core.MethodCall._getAsTemplateModel(MethodCall.java:93)
How can I resolve #spring.formInput in #list.
Have you tried doing an intermediate assign? I saw this problem on other StackOverflow pages, like Freemarker syntax for a form for a collection of objects (Spring 3 MVC):
<#list flowList as flow>
<#assign flowDate = flow.createDatetime />
<#spring.formInput "flowDate" />
<\#list>
The following workaround works for me, but is pretty ugly:
<#list flowList as flow>
<#assign index=flowList?seq_index_of(flow)>
<#spring.formInput "flowList[${index}].createDatetime" />
</#list>
When the above form is posted, you'll need to ensure that the flow-list is pre-populated with empty flows. Alternatively, just using Spring's AutoPopulatingList as the flow-list implementation.
For spring to bind the object, the exact reference must be provided. Hence you need to add the index in the tag. This is needed when you post the form back and want the flowlist object as request body in a controller method.
<#list flowList as flow>
<#spring.formInput "flowList[${flow_index}].createDatetime" />
</#list>
After rendering if you look at the HTML it would be like
<input type="text" id="flowList0.createDatetime" name="flowList[0].createDatetime" value="..." />