I'm trying to create a thymeleaf form to update just a couple of attributes of the backing object:
@RequestMapping(value = "/jobs/{id}", method = RequestMethod.GET)
public ModelAndView update(@PathVariable Integer id ) {
ModelAndView mav = new ModelAndView("updateJob.html");
JobDescription updateJob = jobDescriptionService.findByID(id);
mav.addObject("updateJob", updateJob);
return mav;
}
@RequestMapping(value = "/jobs/{id}", method = RequestMethod.PUT)
public String saveUpdate(@PathVariable Integer id, @ModelAttribute("updateJob") JobDescription updateJob) {
jobDescriptionService.update(updateJob);
return "redirect:/jobs/" + id;
}
<form th:action="@{'/jobs/'+ ${updateJob.id}}" th:object="${updateJob}" th:method="PUT">
<table>
<tr>
<td><label>Description</label></td>
<td><input type="text" th:field="*{description}" /></td>
</tr>
<tr>
<td><label>Deadline</label></td>
<td><input type="text" th:field="*{deadline}" /></td>
</tr>
<tr>
<td></td>
<td><button type="submit">Update</button></td>
</tr>
</table>
</form>
The problem is that the job object has a couple of other attributes(like id, createdDate, etc) which I don't want to update. However, when I click the submit button of the update form, the object created in the saveUpdate method has those attributes set to null(unless I set them in hidden fields inside the form). Is there any other way I could keep them?
I have the same problem as you, so I've built my own solution
1- You need two action on controller : view (GET) and action (POST)
@GetMapping("/user/edit/{userId}")
public ModelAndView editUserView(@PathVariable Long userId) throws NotFoundException {
User user = this.userService.load(userId);
if (user == null) {
throw new NotFoundException("Not found user with ID " + userId);
}
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("user.edit");
modelAndView.addObject("user", user);
return modelAndView;
}
@PostMapping("/user/edit/{userId}")
public ModelAndView editUserAction(HttpServletRequest request, @PathVariable Long userId, @Validated(User.ValidationUpdate.class) User userView,
BindingResult bindingResult) throws Exception {
User user = this.userService.load(userId);
if (user == null) {
throw new NotFoundException("Not found user with ID " + userId);
}
ModelAndView modelAndView = new ModelAndView();
if (bindingResult.hasErrors()) {
modelAndView.setViewName("user.edit");
modelAndView.addObject("user", userView);
return modelAndView;
}
Form.bind(request, userView, user);
this.userService.update(user);
modelAndView.setViewName("redirect:/admin/user");
return modelAndView;
}
2- One view with error display (verry important : add hidden input to send id for validation)
<fieldset th:if="${#fields.hasErrors('${user.*}')}" class="text-warning">
<legend>Some errors appeared !</legend>
<ul>
<li th:each="err : ${#fields.errors('user.*')}" th:text="${err}"></li>
</ul>
</fieldset>
<form action="#" th:action="@{/admin/user/edit/{id}(id=${user.id})}" th:object="${user}" method="post">
<div th:class="${#fields.hasErrors('firstName')} ? 'form-group has-error' : 'form-group'">
<label class="control-label" for="firstName">First Name <span class="required">*</span></label>
<input type="text" th:field="*{firstName}" required="required">
</div>
...
<input type="hidden" th:field="*{id}">
</form>
3- For my example, i wrote a FormUtility class to merge two object :
public static List<String> bind(HttpServletRequest request, Object viewObject, Object daoObject) throws Exception {
if (viewObject.getClass() != daoObject.getClass()) {
throw new Exception("View object and dao object must have same type (class) !");
}
List<String> errorsField = new ArrayList<String>();
// set field value
for (Entry<String, String[]> parameter : request.getParameterMap().entrySet()) {
// build setter/getter method
String setMethodName = "set" + parameter.getKey().substring(0, 1).toUpperCase()
+ parameter.getKey().substring(1);
String getMethodName = "get" + parameter.getKey().substring(0, 1).toUpperCase()
+ parameter.getKey().substring(1);
try {
Method getMethod = daoObject.getClass().getMethod(getMethodName);
Method setMethod = daoObject.getClass().getMethod(setMethodName, getMethod.getReturnType());
setMethod.invoke(daoObject, getMethod.invoke(viewObject));
}
catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException exception) {
errorsField.add(parameter.getKey());
}
}
return errorsField;
}
Hope this help you.
The safest way to do this is to load original job object by id, and set new values on it and then to update it... Something like:
JobDescription originalJob = jobDescriptionService.findById(updateJob.getId());
originalJob.setParamForUpdate(updateJob.getParamForUpdate());
originalJob.setAnotherParamForUpdate(updateJob.getAnotherParamForUpdate());
jobDescriptionService.update(originalJob);
And this will save all data you want to keep unchanged...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With