modelAttribute in mustache Templates (Spring-Boot App) / binding form data - spring

I'm working on a Spring-Boot app which handles form data. My question is, if there is a possibility to bind the form data in case of validation errors for example.
My case:
form.mustache:
<form action="/form/basisdata" method="post" name="basisdata">
<label for="contactName">Kontakt / Ansprechpartner*:</label>
<input type="text" name="contactName"/>
<label for="emailAddress">E-Mail-Adresse*:</label>
<input type="text" name="emailAddress"/>
<input type="hidden" name="_csrf" value="{{_csrf.token}}" />
<button class="a-button" type="submit">Weiter</button>
</form
Controller method:
#PostMapping("/basisdata")
public ModelAndView setFormBasisdata(#Valid #ModelAttribute("basisdata") Basisdata basisdata, BindingResult bindingResult, Map<String, Object> model) {
if (bindingResult.hasErrors()) {
List<Error> errorList = getErrors(bindingResult);
model.put("errors" , errorList);
model.put("basisdata", basisdata);
return new ModelAndView("formBasisdata", model);
}
return new ModelAndView("formNextStep", model);
}
In case of validation errors we're returning correctly to the same form page and shows the list of errors. But the form stays empty. I would prefer it, if the form is prefilled with the values that were entered before.
Is returning ModelAndView in that case the wrong option?
In jsp templates using spring forms there is an attribute in the form, that specifies the model (modelAttribute) in thymeleaf it's th:object. Is there something that I need to change / add in my mustache template?

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 do I lose information after submit a form with Spring MVC?

As I say int the title I loose information in the object that comes back from JSP to Controller.
From my Controller I pass a ModelAndView with an object of class Historic.
In the JSP page I have access to all of the values of this object, but when I submit I just get part of this information, some looses on the way on.
Controller:
#GetMapping("/tt")
public ModelAndView index(Model model) {
HistoricBO historic = new HistoricBO();
// ... I fulfill this object ...
return new ModelAndView("tt", "historic", historic);
}
In JSP I have access to all the information that I passed.
I use the values in two different ways. The first one (information that later I won't be able to recover) is:
<form:form method="POST" action="/addInput" modelAttribute="historic">
....
<form:label path="userHistoric[0].user.name" />
<form:input path="userHistoric[0].user.name" disabled="true" />
Being userHistoric a list inside HistoricBO object.
And the other way that I use the object values is daoing loop to the registers and show them. I can have these values after submit:
c:forEach items="${historic.userHistoric[0].periods[0].registers}" var="reg" varStatus="rog">
...
<td class="tab-odd">
<form:input path="userHistoric[0].periods[0].registers[${rog.index}].hours[0]" class="monin" type="number" />
</td>
The method that catch the submit is as follows:
#PostMapping("/addInput")
public String savePeriod(
#ModelAttribute("historic") HistoricBO inputs,
BindingResult result, ModelMap model) {
if (result.hasErrors()) {
return "error";
}
...
And here the object inputs only has setted the hours values, the rest of the object is empty.
Can you please why is the info loosing and how to solve it?
Thanks
Remove disabled="true" and use readonly="true" or readonly="readonly" instead like below.
<form:input path="userHistoric[0].user.name" readonly="readonly" />
Disabled values will not be submitted with the form.
See this values-of-disabled-inputs-will-not-be-submitted and demo here.

How to set an attribute into a session with thymeleaf?

I'm trying to add an attribute to an HTTP session with thyme leaf, but I can't. I had a "Project" object and I need to pass it to the controller. I had a form in the page, so I've tried this, but it doesn't work:
<input type="hidden" th:attr="${#session.setAttribute('proyecto', '${proyecto}')}"
The controller receives the attribute proyecto but the value is "${proyecto}", not the content of the project object
Send the value using a form and then set the session attribute in your controller.
HTML
<form th:action="#{/newProject}" th:object="${newProject}" method="post">
<input th:field="*{id}"></input>
</form>
Controller
#RequestMapping(value = "/newProject", method = RequestMethod.POST)
public String addProject(HttpSession session, #ModelAttribute("newProject") Project project) {
session.setAttribute("proyecto", project);
return "/newPage";
}
This is just example of course. You would need to add all the project fields in your HTML.

Spring relationships between tables in view

Well, I started a new project using Spring MVC (I'm beginner in technology) and soon I had a basic question which I am unable to find on the internet, maybe the reason that I'm doing wrong or implementing the wrong question.
I have a form in which the data will be persisted are in two different tables.
What better way to do this?
I created two related tables, one called "Agency" and another called "Login". An "Agency" may contain one or more "Login" (# OneToMany), but the problem takes the view creation time, because data from both tables will compose a single form. With some research I noticed that I can not have two modelAttribute in my form.
I apologize for the mistakes in English.
Best regards!
if the mapping is correct and Agency contain one or many login, what you have to do is render the Agency in the model and view and in your form iterate the logins
<form:form id="foo"
method="post"
action="url"
modelAttribute="agency">
<form:input type="hidden" path="id"/>
<c:forEach var="login" items="${agency.logins}"
varStatus="login_index">
<form:input type="hidden" path="login.id" />
</c:foreach>
</form:form>
thanks for the reply.
But is not it :(
I have a form in which the data will be persisted are in two different tables.
<form class="form-signin" method="post" action="addAgency">
<div class="input-group">
<span class="input-group-addon entypo-user"></span>
//Table Agency
<spring:bind path="tenant.firstName"/>
<input class="form-control" placeholder="Nome"/>
//Table Login
<spring:bind path="login.email"/>
<input class="form-control" placeholder="Nome"/>
</div>
//Rest of my form...
</form>
In my view I have the annotation "bind" Spring, searching in the internet I found this way to make the connection between the controller and the view for persist two tables.
#RequestMapping(value = "/", method = RequestMethod.GET)
public String home(#ModelAttribute("tenant") Agency tenant, #ModelAttribute("login") Login login, ModelMap map) {
Agency agency = dashboardFacade.getAgency();
map.addAttribute("agency", agency);
if (tenantResolver.isMasterTenant()) {
//Here is the problem!!
// Add an attribute in my view of type login and agency, but i don't kwon if it is correct.
map.addAttribute("tenant", tenant);
map.addAttribute("login", login);
return "landing/index";
} else {
return "dashboard/home";
}
}
The method below to save an agency and login.
// Add a new agency
#RequestMapping(value = "/addAgency", method = RequestMethod.POST)
public String addAgency(#ModelAttribute("tenant") Agency agency, #ModelAttribute("login") Login login, Model model, final RedirectAttributes redirectAttributes) {
agency = dashboardFacade.addAgency(agency);
login = dashboardFacade.addLogin(login);
return "redirect:" + getAgencyFullUrl(agency);
}
What better way to do this?
Thank you

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