Propagate object in thymeleaf form - java

I have a page with two forms, and want to add object from first form when submitting second one. Basically it looks like this:
First form:
<form action="#" method="POST" enctype="multipart/form-data" th:action="#{/foo}" th:object="${prop1}" id="form1">
<input type="file" name="file" class="form-control"/>
<input type="text" th:value="${prop1.taskSheetName}" name="taskSheetName"/>
<input type="number" th:value="${prop1.descriptionColumnPosition}" name="descriptionColumnPosition"/>
</form>
Second form:
<form th:if="${resourceId}" action="#" method=" th:object="${prop2}" id="prop2" th:action="#{/foo/{id}(id=${resourceId})}">
<input type="url" th:value="${prop2.url}" name="url"/>
<input type="text" th:value="${prop2.username}" name="username"/>
<input type="password" th:value="${prop2.password}" name="password"/>
</form>
And controller methods:
#RequestMapping(value = "/foo", method = POST)
public String uploadFile(#RequestParam("file") MultipartFile file, final ExcelProperties properties, final Model model) {
//some logic here
}
#RequestMapping(value = "/process/{id}", method = POST)
public String processResource(#PathVariable("id") String id, final Prop2 prop2, final Prop1 prop1, final Model model) {
}
And I need to pass the values from first form values to second method with second form values, but it passes empty form object. I tried to add
<div th:with="p1=${prop1}">
since I know that this data is present in page, but it didn't help.
If this even possible or should I just give up and write custom submit handler in javascript?

According to MDN, it's impossible to attach 1 input into 2 forms,
You need to do some work with JavaScript.

Related

#ModelAttribute return null thymeleaf

I familiarize myself with Thymeleaf.
I am trying to get data from a POST-mapping in the controller.
But I got null variables when I call employee.getName().
I looked a lot online but can't find a solution.
My controller class
#GetMapping
public String getEmployees(Model model) {
model.addAttribute("newEmployee", new Employee());
return "employees";
}
#PostMapping
public String addEmployee(#ModelAttribute("newEmployee") Employee employee) {
System.out.println(employee); // return object
System.out.println(employee.getFirstName()); // return null
return "employees";
}
HTML form with Thymeleaf
<div class="modal-body">
<form th:action="#{/employees}" th:object="${newEmployee}" method="post">
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label class="col-form-label">First Name <span class="text-danger">*</span></label>
<input th:field="*{firstName}" class="form-control" type="text">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label class="col-form-label">Last Name</label>
<input th:field="*{lastName}" class="form-control" type="text">
</div>
</div>
See tutorials:
Wim Deblauwe: Form handling with Thymeleaf
Baeldung: Binding a List in Thymeleaf
#GetMapping("/all")
public String showAll(Model model) {
model.addAttribute("employees", employeeService.findAll());
return "employees/all";
}
#GetMapping("/add")
public String createForm(Model model) {
model.addAttribute("newEmployee", new Employee()); // the form-data object as blank employee to be filled
return "employees/form"; // the page containing the form
}
#PostMapping
public String addEmployee(#ModelAttribute("newEmployee") Employee employee) {
System.out.println(employee); // return object
System.out.println(employee.getFirstName()); // return null if either not set in HTML-form or not mapped from HTML-form to POST correctly
// save new employee or add to a repository
employeeService.save(newEmployee);
model.addAttribute("employees", employeeService.findAll());
return "redirect:/employees/all"; // use a redirect
}
Client-Server interaction:
First in your browser you should navigate to the endpoint adding the form-data-object, i.e. GET /employees/add.
Then the form-page should load and you will have a blank object newEmployee to bind all the input-fields to for submitting.
After submitting the form via the submit-click, a POST /employees is sent. The server/controller should receive the form-data as model-attribute in employee parameter and can save it.
The controller-method redirects back to the list-endpoint (same as navigating in browser to) GET /employees/all
<h1>Create employee</h1>
<form th:object="${newEmployee}"
th:action="#{/employees}"
method="post">
<input type="text" name="firstName" id="firstName" th:field="*{firstName}" />
<input type="text" name="lastName" id="lastName" th:field="*{lastName}" />
<button type="submit">Submit new employee</button>
</form>
The th:object attribute refers to the key under which we put our Employee instance in the model (newEmployee in this example). The th:action has the URL for the #PostMapping method. Finally, the method attribute is set to "post" since we want to use the HTTP POST method.
Using th:field=*{…​}, we can setup a two-way binding between the HTML input and the field in our form data object.

Save all object with th:object

I want to sent object with model.addAttribute and get all object's variable after saving.
Controller
#GetMapping(value = "/passport/details/{id}")
#PreAuthorize("isAuthenticated()")
public String passportDetails(Model model, #PathVariable(name = "id") Long id) {
PassportRequest passportRequest = businessService.getPassportRequestById(id);
model.addAttribute("passport", passportRequest);
return "/user/passport/create";
}
#PostMapping(value = "/passport/save")
#PreAuthorize("isAuthenticated()")
public String savePassport(PassportRequest passport) {
businessService.savePassportRequest(passport);
return "redirect:/passport/details/" + passport.getId();
}
html-code:
<form enctype="multipart/form-data" method="post" th:action="#{/passport/save}"
data-parsley-validate="" role="form"
novalidate="" th:object="${passport}" id="sign-form">
<input type="hidden" th:field="*{id}">
<input type="hidden" th:field="*{stage}">
<div class="col-6 m-input-group">
<label>Кем командирован</label>
<input type="text" name="postedBy" placeholder="Введите" th:field="*{postedBy}" required>
</div>
</form
The problem is I need to make <input type="hidden" th:field="*{objectVariable}"></input> to every variable to send them. Is there any easier way to do?
One way of doing this is annotating your controller with #SessionAttributes("passport") (which means it will store the object on the session and keep all it's properties during this controllers methods).
You might also need to change
public String savePassport(PassportRequest passport) {
to
public String savePassport(#ModelAttribute("passport") PassportRequest passport) {
to make sure all the attributes line up.

How to pass hidden entity id field through MockMvc?

In my thymeleaf template I add a hidden field for a field on my form object:
<form action="#" th:action="#{/admin/endpoint}" th:object="${monkey}" method="post" class="form-horizontal" role="form">
<input type="hidden" th:field="*{id}" />
<input type="hidden" th:field="*{banana}" />
...
</form>
Created html output:
<input type="hidden" id="id" name="id" value="55">
<input type="hidden" id="banana" name="banana" value="3">
Posting this form through a web browser works as expected, but
when I try to test this with MockMvc only the id field gets set:
mvc.perform(post("/endpoint")
.param("id", String.valueOf(monkey.getId()))
.param("banana", String.valueOf(monkey.getBanana().getId())))
In my controller I let Spring transform the form for me:
#RequestMapping(value = { "/admin/endpoint" }, method = RequestMethod.POST)
public String updateMonkey(Model model, #Valid Monkey monkey, BindingResult br) {
System.out.println(monkey.getId()) // -> 55
System.out.println(monkey.getBanana()) // -> null
...
}
What do I have to change so the field "banana" gets properly initialized from the id in my test?

Form action in Spring MVC

I have a problem in my form action in Spring MVC.
My code:
<form method="POST" action="j_acegi_security_check" name="frmLogin">
<input type='hidden' autocomplete="off" name='_schema' id="_schema"/>
<input type='hidden' id='j_username' name='j_username' autocomplete="off" />
<input type='hidden' id='j_password' name='j_password' autocomplete="off" />
</form>
I can't understand what kind of action is j_acegi_security_check perfoming.
For Form Action, you need to land your request to the controller you created for. Specify your request name for example
This is HTML part <form:form method = "POST"action="/addUser">...</form:form>
For your controller class
#Controller
public class UserController {
#RequestMapping(value = "/addUser", method = RequestMethod.POST)
public String addStudent(#ModelAttribute("SpringWeb")User user,
ModelMap model) {
}
Your request will be landed to this controller where you can perform any desired action.
Hope this will help.

Input/Textarea - Required string parameter is not present

I'm having issues with the above code recognizing the 'defaultpagination' variable. It keeps throwing the 'Required String parameter 'defaultpagination' is not present' message on the user interface. This is part of an edit screen showing a configurable setting.
The controller method is below.
#RequestMapping(value = "/update", method = RequestMethod.POST)
public ModelAndView updateSetting(
#RequestParam("defaultpagination") String defaultPagination,
#RequestParam("id") long id) {
Setting setting = settingService.getOne(id);
setting.setDefaultPagination(Integer.parseInt(defaultPagination));
settingService.saveSetting(setting);
return new ModelAndView("redirect:/settings/setting-listing");
}
The thymleaf html can be seen below.
<form role="form" data-toggle="validator" name="createSetting" enctype="multipart/form-data" method="post" th:object="${setting}" action="/settings/update">
<div class="form-group">
<label for="defaultpagination">Default Pagination</label>
<input class="form-control" name="defaultpagination" type="number" th:field="${setting.defaultPagination}" placeholder="Enter default pagination" required="required" data-error="Pagination is required (maximum cannot exceed 100)" />
<div class="help-block with-errors"></div>
</div>
<input type="hidden" th:rows="1" class="form-control" id="id" name="id" th:value="${setting.id}"/>
<br />
<button type="submit" class="btn btn-default">Submit Button</button>
<br />
<br />
<br />
</form>
What's interesting is that I can change the 'input' tag to a textarea tag and everything would work as normal (the defaultpagination gets recognized). However, I want to use the input for validation. In this case the input needs to be a number. In similar cases, I will need a phone number and email. Has anyone faced this same problem regarding the input/textarea tag in thymeleaf?
one solution would be to change the String defaultPagination in the controller to Integer (if you expect only integer as defaultpagination)
ie
#RequestMapping(value = "/update", method = RequestMethod.POST)
public ModelAndView updateSetting(
#RequestParam("defaultpagination") Integer defaultPagination,
#RequestParam("id") long id) {
Another solution is to use the pattern attribute of the input tag (permit only numbers not decimal points)
<input class="form-control" name="defaultpagination" type="text"
pattern="\d+" th:field="${setting.defaultPagination}" placeholder="Enter default
pagination" required="required" data-error="Pagination is required (maximum cannot exceed 100)" />
Let me know if these worked for you

Categories