How pass params to Thymeleaf Ajax Fragment - ajax

I have a Spring MVC controller that returns the name of a thymeleaf fragment to view resolver bean. The problem is that this fragment needs a url as a parameter. Here I put the fragment:
<!-- A fragment with wrapper form for basic personal information fragment -->
<th:block th:fragment="form-basic(url)">
<form role="form" th:action="${url}" method="post" th:object="${user}">
<th:block th:replace="admin/fragments/alerts::form-errors"></th:block>
<th:block th:include="this::basic" th:remove="tag"/>
<div class="margiv-top-10">
<input type="submit" class="btn green-haze" value="Save" th:value="#{admin.user.form.save}" />
<input type="reset" class="btn default" value="Reset" th:value="#{admin.user.form.reset}" />
</div>
</form>
</th:block>
I can not get a way to pass that parameter without getting an error.
The controller is as follows:
#RequestMapping(method = RequestMethod.GET)
public String show(#CurrentUser User user, Model model) {
logger.info(user.toString());
if(!model.containsAttribute(BINDING_RESULT_NAME)) {
model.addAttribute(ATTRIBUTE_NAME, user);
}
model.addAttribute("url", "/admin/users/self/profile");
return "admin/fragments/user/personal::form-basic({url})";
}
For the above example I get the following error:
06-Jan-2017 11:36:40.264 GRAVE [http-nio-8080-exec-9] org.apache.catalina.core.StandardWrapperValve.invoke El Servlet.service() para el servlet [dispatcher] en el contexto con ruta [/ejercicio3] lanzó la excepción [Request processing failed; nested exception is java.lang.IllegalArgumentException: Invalid template name specification: 'admin/fragments/user/personal::form-basic({url})'] con causa raíz
java.lang.IllegalArgumentException: Invalid template name specification: 'admin/fragments/user/personal::form-basic({url})'
at org.thymeleaf.spring4.view.ThymeleafView.renderFragment(ThymeleafView.java:275)
at org.thymeleaf.spring4.view.ThymeleafView.render(ThymeleafView.java:189)
at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1257)
at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1037)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:980)
I have done these tests:
"admin/fragments/user/personal::form-basic('{url}')";
"admin/fragments/user/personal::form-basic(#{/admin/users/self/profile})";
"admin/fragments/user/personal::form-basic(/admin/users/self/profile)";
"admin/fragments/user/personal::form-basic('/admin/users/self/profile')";
In all I get error

You have two ways for pass parameter from controller to Thymeleaf fragment. First is the usual Spring way - throw the model:
#RequestMapping(method = RequestMethod.GET)
public String show(#CurrentUser User user, Model model) {
model.addAttribute("url", "/admin/users/self/profile");
return "admin/fragments/user/personal::form-basic";
}
That's enough. In this case you don't need specify any fragment parameters (even if you have it).
Second way is specify parameters in fragment name:
#RequestMapping(method = RequestMethod.GET)
public String show(#CurrentUser User user, Model model) {
String url = "/admin/users/self/profile";
return String.format("admin/fragments/user/personal::form-basic(url='%s')",url);
}
Note, that name of parameter must be specified, and the string value must be placed in single quotes. In this case you don't need add url variable into model.

All you need to do is include your url parameter as model attribute (as you did). There is no need to notifying your fragment name in any way. Just return fragment name as it would have no parameters.
// rest of method body omitted
model.addAttribute("url", "/admin/users/self/profile");
return "admin/fragments/user/personal::form-basic";
Btw. I see that your url lead to some other endpoint. In that case you should use it in #{...} manner in your th:action attribute of the form.
<form role="form" th:action="#{${url}}" method="post" th:object="${user}">

Related

How to use spring mvc with thymeleaf to iterate over a list?

My goal is to cycle through a list of objects, some to be displayed on the screen, others to be passed into a form as an object of which I can define certain aspects and then return to the controller the object and attribute to be modified.
The problem with the following approach is that the object in the list is not passed correctly to the form and thus gives an error because it is trying to make changes to a non-existent object.
If, on the other hand, I try to pass it as an object via ModelAndView it obviously works but does not have all the characteristics of the object I passed via the list.
Controller
#GetMapping("/")
public ModelAndView home() throws IOException {
ModelAndView mv = new ModelAndView();
mv.setViewName("home");
List<Comics> allComics = cs.getAll();
mv.addObject("comics", allComics);
return mv;
}
#PostMapping("/update")
public ModelAndView update(Comics com, #RequestParam("attr") String attr) throws IOException {
ModelAndView mv = new ModelAndView();
com.setLastRead(attr);
cs.updateAttributes(com);
mv.setViewName("home");
List<Comics> allComics = cs.getAll();
mv.addObject("comics", allComics);
return mv;
}
home.html
<html xmlns:th="http://www.thymeleaf.org">
<tr th:each="comic : ${comics}">
<td th:text="${comic.title}"></td>
<td th:text="${comic.lastChapter}"></td>
<td>
<a th:href="${comic.lastChapterLink}" target="_blank"
role="button" class="btn btn-md btn-block btn-info"> Link
</a>
</td>
<td></td>
<td>
<form th:action="#{/update}" th:object="${comic}" method="post">
<input type="text" name="attr" id="attr"/>
<button type="submit">Sub</button>
</form>
</td>
</tr>
PS: I cut out the head of the html page because it was full of non-relevant CDNs
How can I integrate Spring MVC with Thymeleaf to achieve the result whereby passing a list of objects can be displayed on the screen and used for other purposes within the html page without throwing errors?
Obviously if you know of more efficient methods to achieve the result I'm listening; I only used this method because I didn't know of any others.
Thank you
Answer to #RafaeldaSilva:
I agree, but that does not solve the problem.
Let me explain: the attribute I am going to modify through the form already has its name to allow what you wrote.
But the object iterated through:
tr th:each="comic : ${comics}">
cannot be passed directly as input, as it is a value that is taken from a list and exists individually only in the html page.
One might think of passing it as hidden input, but in this case the result would be the same (I have tried):
<form th:action="#{/update}" th:object="${comic}" method="post">
<input type="hidden" value="${comic}" name="com"/>
<input type="text" name="attr" id="attr"/>
<button type="submit">Sub</button>
</form>
#PostMapping("/update")
public ModelAndView update(#RequestParam("com") Comics com, #RequestParam("attr") String attr) throws IOException {
ModelAndView mv = new ModelAndView();
com.setLastRead(attr);
System.out.println("Comic: " + com);
cs.updateAttributes(com);
mv.setViewName("home");
List<Comics> allComics = cs.getAll();
mv.addObject("comics", allComics);
return mv;
}
Error:
[org.springframework.web.bind.MissingServletRequestParameterException: Required request parameter 'com' for method parameter type Comics is present but converted to null]
try removing the type="hidden" to see what is present in this input, as I understand you are inserting an object by doing value="${comic}", this way the input should not send the value wanted..
change this: <input type="hidden" value="${comic}" name="com"/>
for this: <input type="text" value="${comic}" name="com"/>
so you can see what the form is sending to the controller, I believe it's the object's memory path, and not the data that exists in it.
in the input you must inform the attributes of the object, not the complete object..

Why this Spring MVC controller method can't handle this POST Request (Content type 'application/x-www-form-urlencoded' not supported)

I am working on a Spring MVC application and I have the following problem.
Into a FreeMarker view (but is not so important that the view is made using FreeMarker) I have this form:
<form id="reg-form" name="reg-form" action="<#spring.url '/iscrizioneStep2' />" method="post">
<fieldset>
<div class="form-group">
<label for="cf">Codice fiscale:</label>
<input type="text" id="cf" name="codiceFiscale" class="form-control input-mm" placeholder="Inserisci il tuo codice fiscale" data-validation="[NOTEMPTY, NOSPACE, L==16, CF]" data-validation-label="codice fiscale" aria-required="true" tabindex="10">
</div>
<div class="form-group">
<div class="g-recaptcha" data-sitekey="6LcOfhcTAAAAAE3D2hsa3UcyTQ0PI4upcZ759FDa" tabindex="20"></div>
</div>
<button type="submit" class="btn btn-block submit-btn" aria-label="prosegui la registrazione" tabindex="30">Passaggio 2</button>
</fieldset>
</form>
As you can see the form is submittet to this action: action="<#spring.url '/iscrizioneStep2' />" performing a POST request.
This generate a POST request toward this URL (I see it using FireBug):
http://localhost:8080/iam-ssum-public/iscrizioneStep2?codiceFiscale=AAAAAAAAAAAAAAAA&g-recaptcha-response=
So I think that it should send the input field having name="codiceFiscale" into the POST request.
Then I have this controller method:
#RequestMapping(value = "/iscrizioneStep2", method = RequestMethod.POST)
public String iscrizioneStep2(#RequestBody(required = true) IscrizioneStep1Form iscrizioneStep1Form, Model model)
throws APIException {
/*
* Verifica se l'utenza è attivata per il codice fiscale
*/
String codiceFiscale = iscrizioneStep1Form.getCodiceFiscale();
..............................................................
..............................................................
..............................................................
return "myView";
}
So the data sended in the post request should be putted inside the IscrizioneStep1Form iscrizioneStep1Form parameter, that is:
public class IscrizioneStep1Form {
/**
* Codice fiscale
*/
private String codiceFiscale;
public String getCodiceFiscale() {
return codiceFiscale;
}
public void setCodiceFiscale(String codiceFiscale) {
this.codiceFiscale = codiceFiscale;
}
}
But the problem is that this HTTP POST request is not handled by the iscrizioneStep2() method. When I submit the form don't enter into this method and into the Eclipse console I obtain the following error message:
11:55:43,949 WARN [org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver] (http-localhost/127.0.0.1:8080-6) Handler execution resulted in exception: Content type 'application/x-www-form-urlencoded' not supported
Why? What am I missing? How can I fix this issue?
Try to add heders param to your RequestMapping annotation:
#RequestMapping(value = "/iscrizioneStep2", method = RequestMethod.POST,
headers = "content-type=application/x-www-form-urlencoded")
Try to remove #RequestBody(required = true) from your request method.
Here Spring Mvc Rest Webservice jstl form submittion HTTP Status 415 Content type 'application/x-www-form-urlencoded' not supported has the same problem.
There is no built-in converter that knows how to convert content of the type 'application/x-www-form-urlencoded to IscrizioneStep1Form. Simply omit #RequestBody. Form data is automatically mapped to objects.
The problem is that when we use application/x-www-form-urlencoded, Spring doesn't understand it as a RequestBody. So, if we want to use this
we must remove the #RequestBody annotation.
Then try the following:
#RequestMapping(value = "/iscrizioneStep2", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public String iscrizioneStep2(IscrizioneStep1Form iscrizioneStep1Form, Model model) throws APIException {
//method body where
}
Note that removed the annotation #RequestBody
answer: Http Post request with content type application/x-www-form-urlencoded not working in Spring

Spring MVC: How do I preserve model attributes in spring validation errors

I searched around on Stack Overflow, but could not find the solution to my query. I have a controller function that adds multiple model attributes on a GET request
#RequestMapping(method = RequestMethod.GET, value = "/showdeletesearchqueryform")
public String showDeleteSearchQuery(final Model model) {
if (LOG.isDebugEnabled()) {
LOG.debug("Fetching all the search query results.");
}
ImmutableList<ArtQueryResults> results = this.searchQueriesService
.getSearchQueries(APPNAME);
// Adding model attribute # 1
model.addAttribute("searchResults", results);
if (LOG.isDebugEnabled()) {
LOG.debug("\"searchResults\" model attribute has been intialized from "
+ results);
}
ArtDeleteQueryRequest request = new ArtDeleteQueryRequest();
request.setAppName(APPNAME);
if (LOG.isDebugEnabled()) {
LOG.debug("Model attribute initialized = " + request);
}
// Adding model attribute # 2
model.addAttribute("deletedAttributes", request);
return "deletesearchqueries";
}
My JSP
<div class="column-group">
<form:form method="POST" action="${pageContext.request.contextPath}/arttestresults/showdeletesearchqueryform" modelAttribute="deletedAttributes">
<form:errors path="*" cssClass="alert alert-danger column lg-units-5 units-2" element="div"/>
<form:hidden path="appName" id="appNameId" htmlEscape="true"/>
<div class = "units-1 column lg-units-12">
<!-- Hidden Key for app name. -->
<form:select path="idsToBeDeleted" id="IdsToBeDeletedSelectId">
<c:forEach items="${searchResults}" var="searchResult" varStatus="loop">
<form:option label="${searchResult.searchQuery}" value="${searchResult.id}" />
</c:forEach>
</form:select>
</div>
<div class="units-1 column lg-units-12">
<%-- This is a hack that make sure that form is submitted on a click. Not sure why form is not being submitted. --%>
<button class="button" type="submit" onclick="javascript:$('form').submit();">Delete Selected Queries</button>
</div>
</form:form>
My controller POST function
#RequestMapping(method = RequestMethod.POST, value = "/showdeletesearchqueryform")
public String deleteSearchQueries(
Model model,
#ModelAttribute(value = "deletedAttributes") #Valid final ArtDeleteQueryRequest request,
final BindingResult result) {
if (result.hasErrors()) {
LOG.warn("There are " + result.getErrorCount() + " validation errors.");
return "deletesearchqueries";
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("The ids to be deleted are " + request.getIdsToBeDeleted());
}
this.searchQueriesService.deleteSearchQueriesById(
ImmutableList.copyOf(request.getIdsToBeDeleted()));
return "redirect:/arttestresults/showdeletesearchqueryform";
}
}
If there is a validation failure, the model attribute searchResults is not being picked up when I return a view on error condition? Is there a way to preserve the other defined model attributes as well?
Seems that you need flash attributes which were added in spring 3.1. Please take a look at example/explanation:
http://viralpatel.net/blogs/spring-mvc-flash-attribute-example/
The get and the post are different requests. What you get in the post request, is only what comes from the form, so only the "deletedAttributes" model attribute and only the fields that are <input> in the JSP.
You need to put again the searchResults model attribute explicitely like you did in get method.
As suggested by M. Deinum, if one or more attribute(s) will be used by all methods in a controller, you can use a #ModelAttribute annotated method to put it (them) in model automatically.
You can also use SessionAttributes model attributes, that is attributes that are stored in session and not in request. But it is hard to have them properly cleaned from session if user do not post the form but go into another part of the application. You have an example of usage ofSessionAttributes` in Spring's Petclinic example.

How to pass a default value to a Spring input/hidden element?

Suppose, I have the following Spring form
<form:form id="mainForm" name="mainForm"
method="post" action="Temp.htm" commandName="tempBean">
<form:hidden path="stringValue" />
</form:form>
The hidden field is mapped with the command bean - TempBean. What if, I need to pass a default value which is dynamic to this hidden field and dependent upon some other operations?
HTML context:
<c:set var="someVariable" value="${someValue}"/>
<input type="hidden"
id="stringValue"
name="stringValue"
value="${someVariable}"/>
The tags like <form:input> and <form:hidden> don't have a value attribute. So, how to pass a default value to a command object in this scenario?
I'm using Spring 3.2.0.
You can set the default value to the bean you are using in common name
#RequestMapping(value="/someView.html", method=RequestMethod.GET)
public String someView(ModelMap modelMap){
TempBean tempBean = new TempBean();
tempBean.setStringValue(somevalue);
modelMap.addAttribute(tempBean );
return "something";
}

how to do binding in Spring annotated request parameter?

i have a controller that is using annotation for request mapping and requestParam.
the controller is working fine. However when submitting a command object with array, spring will crap out saying array index out of bound. i am guessing there is something wrong with binding but don't know how to fix it.
to be more specific, in eclipse i would set debugger at the beginning of the controller, and when submitting the form (by hitting a input submit button) eclipse debugger will not trigger and i will see array index out of bound error in console.
the controller is something like this:
#RequestMapping(value = {"/internal/pcsearch.dex", "/external/pcsearch.dex"},
method = {RequestMethod.POST, RequestMethod.GET})
public ModelAndView executeProductCatalogSearch(
HttpServletRequest request,
#RequestParam(value = "cat" ,required = false) String cat,
#RequestParam(value = "brand" ,required = false) String brand,
#ModelAttribute("command") ProductCatalogCommand cmd
){
[edit]
and the jsp is like:
<form name="pForm"
id="pForm"
action="<c:url value="psearch.dex"><c:param name="cat" value="${cat}"/></c:url>"
method="POST"
style="display:inline;">
...
...
<c:forEach var="model" items="${models}" varStatus="modelLinkStatus">
<script>
var modelImg<c:out value="${modelLinkStatus.index}"/>Src = '<c:out value="${model.altModelImage}"/>';
</script>
<spring:bind path="command.models[${modelLinkStatus.index}].modelSkusDisplayed">
<input type="hidden" name="<c:out value="${status.expression}"/>" id="<c:out value="${status.expression}"/>" value="<c:out value="${status.value}"/>"/>
</spring:bind>
<spring:bind path="command.updateCartButton">
<input type="submit" value="<spring:message code="orderEntryMessages.ecatalog.button.addToCart" text="Add to Cart" htmlEscape="yes" />" name="<c:out value="${status.expression}"/>" id="<c:out value="${status.expression}"/>" class="sub_buttons"/>
</spring:bind>
...
and the command object declare the model array as:
private List<ModelLink> models = new ArrayList<ModelLink>();
where modelLink is a custom ds.
the first foreach tag handle the the model command object and the 2nd part is the submit button i clicked on.
i think you should use AutoPopulatingList as models to bind list to view and controller. for example please refer link. This might resolve your problem of index.

Resources