Pass through variable in Spring boot / Thymeleaf - spring-boot

I am looking for a way to pass the whole object through without having to use <input type="hidden" /> on the different variables. It seems like the th:object will not carry over the incoming information on the "whole object"
<form action="#" th:action="#{/api/result/save}" th:object="${result}" th:method="post">
<!--- Input fields -->
<input type="hidden" th:field="${result}"> <---- Not working.
<button type="submit" class="btn btn-primary" value="spara">Spara</button>
From the model I have
Result result = new Result(teams);
result.setTeam1ID(aTeam1.get().getId()); // This variable will not be changed in the HTML so I would like to pass that to the next page
// Other variables
When I get to the /save the Result will only contain variables set in Thymeleaf it will not retain the information from the original model above.
#PostMapping("/save")
public RedirectView saveResult(Result result, Model model) {
service.saveResult(result);

Thymeleaf is use to generate view, you can store some variable but not the whole object.
Though you can try these method:
If you object is already stored at server-side in memory or data base. You just pass the unique key of that object get it back using hidden input type.
Store the object into session then get it from there whenever requered.

Related

Pass data from Thymeleaf template to springboot controller

I have simple web application written using Springboot and Thymeleaf templates. Report controller receives the data from form and builds the TestPlanReportResponse object which is added as model attribute like this:
#PostMapping("/report")
public String homeSubmit(#ModelAttribute HomeFormInput homeFormInput, Model model, Errors errors) {
final TestPlanReportResponse response = new TestPlanReportResponse(homeFormInput);
model.addAttribute("allData", response);
return "charts";
}
I can work with that data in "charts" thymeleaf template and show the data I need, but I need to send exactly the same object back to controller when button is clicked, but i getting TestPlanReportResponse
object as parameter with nulls set.
#PostMapping("/report/send")
public String sendReport(#ModelAttribute TestPlanReportResponse reportData, Model model) {
//reportData contains just nulls
}
Here is how my button is set in charts template:
<form action="#" th:action="#{/report/send}" th:object="${allData}" method="post">
<button type="submit">Send the report</button>
</form>
So my question is how to send the object back from thymeleaf template? Should i create a hidden input and put there the "allData" object just to send it back? It looks for me like dirty hack. What would be the appropriate way to pass data back? I want to have this app stateless so don't to store the data on a server side.
When I used to work with Spring and Thymeleaf and form, we had the same issue, passing the data back and forth between a form, the template, and different controllers.
And what you suggest is what we did, we used hidden input as dirty as it may look,it was the standard suggested answer, we did not find anything better.
You need to create an input, with a type a value and link it to a field, like this:
<form action="#" th:action="#{/report/send}" th:object="${allData}" method="post">
<input type="hidden" th:value="*{allDataValue1}" th:field="*{allDataField1}" />
//Do this for all your attributes/values that you wish to pass to the controller
<button class="btn btn-info btn-lg btn-block" type="submit">Send the report</button>
</form>
Though, i found this answer, you can try looking into this thread

retain model values in case of error and showing same thymeleaf template

As per my understanding, model attributes are associated with every request and they can not survive multiple requests, until we add them as flashAttributes.
I have a simple controller method which shows a couple of options to user to select from. However, those options are being attached to thymeleaf template using model attributes.
<div class="input-group mb-3" th:each="ingredient : ${recipes.ingredients}">
<div class="input-group-prepend">
<div class="input-group-text">
<input aria-label="Checkbox for following text input" name="ingredient"
th:value="${ingredient.name}" type="checkbox">
</div>
<input aria-label="Text input with checkbox" class="form-control" disabled
th:value="${ingredient.name + ' ' + ingredient.price + 'Rs.'}"
type="text">
</div>
assume "recipes" as model attribute here, which was injected to modelMap inside the controller.
when bean validation fails, below line exectutes.
if (errors.hasErrors()) return "selectItem";
and selectItem template is re-rendered, but whatever model attributes I have set inside previous controller vanishes.
I have solved this using a #ModelAttribute method inside the same controller to set model attributes for every HTTP requests for the specific controller(until it is not in controllerAdvice for global effect).
I am being confused if I am on right way || is there any elegant way to achieve this.
Setting Model attribute for every request is kind of overhead, when I want them to be available for handful of request mappings.
When you say:
selectItem template is re-rendered, but whatever model attributes I
have set inside previous controller vanishes.
You mean that when the page reloads due to validation errors, your model attributes are no longer existing and Thymeleaf probably returns an error, because it cannot find them, correct?
If this is the case, then you have to manually prepare the same model attributes within the if statement (i.e. adding them to your MapModel):
if (errors.hasErrors()) {
map.addAttribute("recipes", recipes);
return "selectItem";
}
Alternatively, if you need this model attribute also on other pages in your controller, you can reduce code duplication by declaring a method with the ModelAttribute annotation, which will add this attribute to all models in your controller:
#ModelAttribute("recipes")
public Recipes loadRecipes() {
// get list of Recipes
return list;
}

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.

Thymeleaf, Spring nested backing object is not binding the values on form submit

I have a nested object and I'm using it as a model for a form.
public AgeBracketSet implements Serializable{
private String id;
private List<AgeBracket> ageBrackets;
/* Getters and Setters */
}
I have successfully bound all the properties of this object to the form and I can visualize their values when the view state is rendered. Here's a simplified version of how I'm doing it with Thymeleaf. Essentially, I loop through the items of the list and get their attributes.
<form id="bracketForm" role="form" th:action="${flowExecutionUrl}" th:object="${ageBracketSet}" method="post">
<input th:id="'bracketSet_'+*{id}" th:field="*{id}" />
<th:block th:each="bracket,loop : *{ageBrackets}" th:id="'bracket_'+${bracket.id}">
<input th:id="'fromAge_'+${bracket.id}" th:field="*{ageBrackets[__${loop.index}__].fromAge}" />
<input th:id="'toAge_'+${bracket.id}" th:field="*{ageBrackets[__${loop.index}__].toAge}" />
</th:block>
</form>
However, when I make changes in the form and submit it, the model remains unchanged. I have confirmed this by debugging the service that receives the form data. The model does not have the changes made in the form. Am I doing anything wrong here?
I am embarrassed to say I've found the solution. The values simply weren't posted to the webflow for a lack of a 'name' attribute in each input. Using the same dynamically generated ID as a name did the job, and the bindings were correct for each item of the list. Like this:
<input th:id="'fromAge_'+${bracket.id}" th:name="'fromAge_'+${bracket.id}" th:field="*{ageBrackets[__${loop.index}__].fromAge}" />
Thanks to everyone who took the time to read this silly post. I'll be more careful next time ;)

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