Reputation: 5097
I would like to know how to create forms that uses th:object
for each object looped in a th:each
. For example, I have the following code.
HTML
<th:block th:each="store: ${stores}">
<form th:object="${store}" th:action="@{/modify-store}">
<input th:field="*{idStorePk}"/>
<input th:field="*{name}"/>
<input th:field="*{phoneNumber}"/>
<button type="submit">Modify</button>
</form>
</th:block>
Controller
@RequestMapping(value = "/stores")
public String getIndex(Model model) {
model.addAttribute("stores", storeService.getAllStores());
return "store";
}
So, I would like to add a form for each object, but it seems that it is not possible and I get the following error.
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'store' available as request attribute
So, I decided to add a @ModelAttribute
in my controller, but can't get to return the actual store.
@ModelAttribute("store")
public Store getStore(Store store) {
return store;
}
With this approach all my forms have null values. I also tried to add a @PathVariable
, but can't see to bind it using th:object
. Is there a solution for this?
Upvotes: 6
Views: 5245
Reputation: 5097
So for anyone stuck at a similar problem. I find out a work around that might help you out. First, you can't use th:object
, it simply won't cut it. Instead, do the following.
<th:block th:each="store: ${stores}">
<form class="store-form" th:action="@{/modify-store}">
<input th:name="idStorePk" th:value="${store.idStorePk}"/>
<input th:name="name" th:value="${store.name}"/>
<input th:name="phoneNumber" th:value="${store.phoneNumber}"/>
<button class="submit-button" type="submit">Modify</button>
</form>
</th:block>
Then just add something similar to the controller.
@PostMapping(value = "/modify-store")
@ResponseBody
public boolean deleteEntry(@ModelAttribute Store store) throws Exception {
// Your code here...
return true;
}
If you want to send it asynchronously then you will need to add some JS
code in order for it to work. It should look something like the code below.
const forms = document.querySelectorAll('.store-form');
forms.forEach(form => {
form.addEventListener('submit', event => {
// Stop the normal form submit triggered by the submit button
event.preventDefault();
const formInputs = form.getElementsByTagName("input");
let formData = new FormData();
for (let input of formInputs) {
formData.append(input.name, input.value);
}
fetch(form.action,
{
method: form.method,
body: formData
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.log(error.message))
.finally(() => console.log("Done"));
});
Upvotes: 6
Reputation: 957
You're sending stores in your controller in model-attribute and on your second controller where you're submitting your form you're using store that's the reason you're getting this error. So correct the spelling error on any one of your controller. Like this :-
@RequestMapping(value = "/stores")
public String getIndex(Model model) {
model.addAttribute("stores", storeService.getAllStores());
return "store";
}
And Your second controller where you're submitting your form will be like this -
@ModelAttribute("stores")
public Store getStore(Store store) {
return store;
}
Upvotes: 0