Thymeleaf Binding list of objects - spring

Here's my Object that I've retrieved from the DB:
#RequestMapping("/category/edit/{id}")
#org.springframework.transaction.annotation.Transactional
public ModelAndView displayCategoryList(#PathVariable("id")Integer id){
ModelAndView mav = new ModelAndView("category-form");
List<CatFeatGroup> catFeatGroupList = catFeatGroupService.findGroupsForCategory(id);
mav.addObject("catFeatGroupList",catFeatGroupList);
return mav;
}
Here's my form.
<form class="form-horizontal">
<div th:each="catFeatGroup,status : ${catFeatGroupList}" class="form-group">
<label>Position</label><input th:field="catFeatGroupList[${status.index}].position" th:value="${catFeatGroup.position}" />
<label>Name</label> <input name="catGroupList[${status.index}].name" th:value="${catFeatGroup.name}" />
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
I need to use th:field to bind that object however this error shows up:
Could not parse as expression: "catFeatGroupList[${status.index}].position"

Add the "__" notation like this
<form class="form-horizontal">
<div th:each="catFeatGroup,status : ${catFeatGroupList}" class="form-group">
<label>Position</label><input th:field="*{catFeatGroupList[__${status.index}__].position}" th:value="${catFeatGroup.position}" />
<label>Name</label> <input data-th-name="*{catFeatGroupList[__${status.index}__].name}" th:value="${catFeatGroup.name}" />
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>

Related

Hidden input data is not passed from view component to post action method in controller - ASP.NET Core 6 MVC

I am trying to pass parameters value from a view component to a post action method, but the hidden input is not received in the controller.
I know how to pass from view to controller and it works fine, but in the view component it is not passed to action.
This is component for show images in Edit view:
public class EditPostImageComponent: ViewComponent
{
private IPostService _postService;
public EditPostImageComponent(IPostService postService)
{
_postService = postService;
}
public async Task<IViewComponentResult> InvokeAsync(ShowPostListItemViewModel showPostListItemViewModel)
{
return await Task.FromResult((IViewComponentResult)View("EditPostImage", _postService.GetPostGallary(showPostListItemViewModel.PostId)));
}
}
This is view component for displaying images:
#model List<DataLayer.Models.ViewModels.Post.ShowPostListItemViewModel>
#foreach (var item in Model)
{
<div class="col-lg-4 mb-4">
#if (item.ImageName != null)
{
<form method="post" asp-area="Admin" asp-controller="Post" asp-action="DeletePostImg">
<input type="hidden" asp-for="#item.PostId" />
<input type="hidden" asp-for="#item.Title" />
<input type="hidden" asp-for="#item.Description" />
<input type="hidden" asp-for="#item.ImageName" />
<div class="blog-item position-relative overflow-hidden rounded mb-2">
<img id="imgPost" class="img-fluid thumbnail" src="/img/post/#item.Title/#item.ImageName" alt="">
<br />
<br />
#*<a asp-controller="Post" asp-action="DeletePostImg"
asp-route-id="#item.PostId" class="btn btn-danger btn-sm">Delete</a>*#
<input type="submit" class="btn btn-danger btn-sm" value="Delete" />
</div>
</form>
}
else
{
<div class="blog-item position-relative overflow-hidden rounded mb-2">
<img id="imgAvatar" class="thumbnail" src="/UserAvatar/Defult.jpg" />
</div>
}
</div>
This is action method for delete images from database directly.
[HttpPost]
public void DeletePostImg(ShowPostListItemViewModel showPostListItemViewModel)
{
_postService.DeletePostImage(showPostListItemViewModel);
}
When I debug, I get a null or zero value for input hidden fields. in view,in similar circumstances these parameters is sent to the controller correctly.
Where is the problem from? Please help me
Thanks a lot
You could press F12 to check the name attribute of the input box,in your case,the name was item.PostId but in your controller the name of the parameter is showPostListItemViewModel ,the name don't match,so it bind failed
You could try as below in your view component:
#foreach (var showPostListItemViewModel in Model)
{
<form method="post" asp-controller="Home" asp-action="Post">
<input type="hidden" asp-for="#showPostListItemViewModel.PostId">
<br />
<input type="hidden" asp-for="#showPostListItemViewModel.Description">
<div class="blog-item position-relative overflow-hidden rounded mb-2">
<input type="submit" value="Submit" />
</div>
</form>
}
The Result:

Model attribute inconsistent after form submission

I'm still trying to figure out what's happening on the backend here. Working with Spring Boot/Thymeleaf. I have a form template that updates a model attribute object on save. I'm trying to use the updated values on the backend, however it's inconsistent among my functions and I'm not sure why.
Thymeleaf template
<div layout:fragment="content" class="container">
<form action="#" th:action="#{/utility/exportbadges}" th:object="${badgeExport}" method="post">
<div class="form-group col-md-6">
<label class="col-form-label-sm">Badge type</label>
<select th:field="*{type}" class="form-control">
<option th:each="badgeType : ${T(org.myorg.myproject.model.badge.BadgeType).values()}"
th:value="${badgeType}"
th:text="${badgeType}">
</option>
</select>
</div>
<div class="form-group col-md-3">
<label class="col-form-label-sm">Use background?</label>
<input type="checkbox" th:field="*{background}" class="form-control">
</div>
<div class="form-group col-md-3">
<label class="col-form-label-sm">Mark preprinted?</label>
<input type="checkbox" th:field="*{preprinted}" class="form-control">
</div>
<div class="form-group col-md-3">
<label class="col-form-label-sm">Save to path (/tmp default):</label>
<input type="text" th:field="*{saveDir}" class="form-control" placeholder="/tmp">
</div>
<div class="form-group col-md-12 mt-2">
<div class="col-sm-10">
<input class="btn btn-primary" id="save" type="submit" value="Export" />
<input class="btn btn-secondary" type="reset" value="Reset" />
</div>
</div>
</form>
</div>
#RequestMapping(value = "/utility/exportbadges")
public String exportBadges(Model model) {
final BadgeExport badgeExport = new BadgeExport();
model.addAttribute("badgeExport", badgeExport);
return "utility/exportbadges";
}
POST method. The object is correct in this function. Any field that's edited in the form above reflects in this function. However, on redirect, the object is as if it only has default instantiation/has been unedited.
#RequestMapping(value = "/utility/exportbadges", method = RequestMethod.POST)
public String exportBadgeFlow(Model model,
#ModelAttribute("badgeExport") final BadgeExport badgeExport) {
log.info("BadgeExport badge type: {}", badgeExport.getType());
log.info("BadgeExport save dir pre export: {}", badgeExport.getSaveDir());
switch(badgeExport.getType()) {
case "Attendee":
log.error("Attendee export not yet implemented");
break;
case "Vip":
return "redirect:exportbadges/vip-badges.pdf";
case "Specialty":
return "redirect:exportbadges/specialty-badges.pdf";
case "Staff":
return "redirect:exportbadges/staff-badges.pdf";
case "Guest":
return "redirect:exportbadges/guest-badges.pdf";
}
return "redirect:exportbadges";
}
Redirect function. Badge type will be null and saveDir will be /tmp as default instead of updated value in form.
#RequestMapping(value = "/utility/exportbadges/vip-badges.pdf")
public ResponseEntity<String> getAllVipBadgePdf(#ModelAttribute("badgeExport") final BadgeExport badgeExport) throws IOException {
log.info("BadgeExport badge type: {}", badgeExport.getType());
log.info("BadgeExport save dir during export: {}", badgeExport.getSaveDir());
}

Thymeleaf Could not bind form errors using expression "*"

I use Spring-boot and Thymeleaf template engine and I try use th:classappend attribute for adding optional "has-error" class for < div > html tag using #fields.hasErrors('*') expression
<form method="POST" action="/registration" class="form-signin">
<h2 class="form-signin-heading">Create your account</h2>
<div class="form-group" th:classappend="${#fields.hasErrors('*')} ? 'has-error' : ''">
<input name="username" type="text" class="form-control" placeholder="Username" autofocus="true"/>
<p class="alert alert-danger" th:if="${#fields.hasErrors('username')}" th:errors="*{username}"></p>
</div>
<div class="form-group" th:classappend="${#fields.hasErrors('*')} ? 'has-error' : ''">
<input name="password" type="text" class="form-control" placeholder="Password" autofocus="true"/>
<p class="alert alert-danger" th:if="${#fields.hasErrors('password')}" th:errors="*{password}"></p>
</div>
<div class="form-group" th:classappend="${#fields.hasErrors('*')} ? 'has-error' : ''">
<input name="passwordConfirm" type="text" class="form-control" placeholder="Confirm your password" autofocus="true"/>
<p class="alert alert-danger" th:if="${#fields.hasErrors('passwordConfirm')}" th:errors="*{passwordConfirm}"></p>
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit">Submit</button>
</form>
but I have this error
Could not bind form errors using expression "*". Please check this
expression is being executed inside the adequate context (e.g. a
with a th:object attribute)
my controller methods
#RequestMapping(value = "/registration", method = RequestMethod.GET)
public String registration(Model model) {
model.addAttribute("userForm", new User());
return "registration";
}
#RequestMapping(value = "/registration", method = RequestMethod.POST)
public String registration(#ModelAttribute("userForm") User userForm, BindingResult bindingResult, Model model) {
userValidator.validate(userForm, bindingResult);
if (bindingResult.hasErrors()) {
return "registration";
}
userService.save(userForm);
securityService.autologin(userForm.getUsername(), userForm.getPasswordConfirm());
return "redirect:/welcome";
}
what am I doing wrong?
I just add th:object="${userForm} attribute to my form element. And now it work fine!

Creating an update page in Spring using Thymeleaf

I'm trying to create an update page for my spring project, When I try to open my edit page using localhost:8080/edit/1 I get
There was an unexpected error (type=Internal Server Error, status=500).
Could not parse as expression: "/edit/{stockNumber}" (editItem:78)
What can I do to solve this issue?
#GetMapping(path="edit/{stockNumber}")
public String editItemForm(#PathVariable Long stockNumber, Model model){
model.addAttribute("item",itemRepository.findOne(stockNumber));
return "editItem";
}
#PostMapping(path="edit/{stockNumber}")
public String editItem(#ModelAttribute Item item){
itemRepository.save(item);
return "redirect:/item";
}
<form action="#" th:object="${item}" th:action="/edit/{stockNumber}" method="post">
<div class="form-group">
<label for="txtItemDesc">Item Description</label>
<input type="text" th:field="*{itemDesc}" class="form-control" id="txtItemDesc" placeholder="item Description" />
</div>
<div class="form-group">
<label for="txtUnit">Unit</label>
<input type="text" th:field="*{unit}" class="form-control" id="txtUnit" placeholder="Unit" />
</div>
<div class="form-group">
<label for="txtAbc">ABC</label>
<input type="text" th:field="*{abc}" class="form-control" id="txtAbc" placeholder="ABC" />
</div>
<button type="submit" value="Submit" class="btn btn-default">Submit</button>
</form>
Your expression in th:action is incorrect. It should be
th:action="'/edit/'+ ${stockNumber}"

While form submitting using ajax..getting Post not supported error ...don't know what is the error?

I am using form submission using AJAX in Spring MVC and Thymeleaf. When I try to submit it it shows
Post method is not supported
I can't figure out the mistake in my code:
<form class="form-horizontal" action="#" th:action="#{/teacher/teacherProfileUpdation}" th:object="${teacherProfileDetailsList}"
id="saveTeacherForm" method="POST" >
<br />
<div class="row">
<div class="col-lg-14 col-md-12">
<br />
<h5 style="margin-left: 15%;">Personal Details</h5>
<hr />
<div class="form-group">
<label class="col-sm-3 control-label">Name</label>
<div class="col-md-3 col-sm-4 col-xs-4">
<input placeholder="Teacher first name" id="txtTeacherFname" th:field="*{firstName}" type="text" class="form-control" />
</div>
<div class="col-md-3 col-sm-4 col-xs-4">
<input placeholder="Teacher middle name" id="txtTeacherMname" th:field="*{middleName}" type="text" class="form-control" />
</div>
<div class="col-md-3 col-sm-4 col-xs-4">
<input placeholder="Teacher last name" id="txtTeacherLname" th:field="*{lastName}" type="text" class="form-control" />
</div>
</div>
</div>
<div class="col-lg-14 col-md-12">
<div class="form-actions">
<input type="hidden" id="hdnStudentByIdInSchoolAdmin" value="0" />
<input type="button" class="btn btn-info pull-right" id="btnUpdateTeacherProfile" value="Save" />
</div>
</div>
</div>
JS:
saveTeacherProfile :function(){
$("#saveTeacherForm").ajaxForm({
success : function(status) {
alert("success");
},
}).submit();
}
Controller:
#RequestMapping(value = "/updateTeacherProfile", method = RequestMethod.POST)
public String updateTeacherProfile( TeacherProfileDetails teacherProfileDetails){
System.out.println("-----------"+teacherProfileDetails.getFirstName()+"-------------");
System.out.println("-----------"+teacherProfileDetails.getLastName()+"-------------");
System.out.println("-----------"+teacherProfileDetails.getMiddleName()+"-------------");
return "success";
}
You are posting the form, but most likely your Spring controller is not configured to accept POST requests. Try this in your server-side Controller class for this page:
#RequestMapping(..., method = { RequestMethod.GET, RequestMethod.POST }, ...)
public void myControllerMethod()
{
#RequestMapping(..., method = { RequestMethod.GET, RequestMethod.POST }, ...)
public String updateTeacherProfile( TeacherProfileDetails teacherProfileDetails){
//ur Logic
}

Resources