The problem
The binding between the inputs
and the List
in the Spring model is done through an index in the input
name. For example:
<form:input path="products[${i.index}].quantidade" id="quantidade${i.index}" />
<form:input path="products[${i.index}].total" id="total${i.index}" />
Your JavaScript logic is probably simply removing elements from form
, eg:
function removeProduct(index) {
var myForm= document.getElementById('form');
var quantidade = document.getElementById('product[' + index + '].quantidade');
var total = document.getElementById('product[' + index + '].total');
myForm.removeChild(quantidade);
myForm.removeChild(total);
}
As a consequence, after removing any element other than the last one from the list, the indexes of inputs
in its form
are messed up,
<input type="text" name="products[0].quantidade" id="quantidade1" value="10" />
<input type="text" name="products[0].total" id="total1" value="10000" />
<input type="text" name="products[3].quantidade" id="quantidade4" value="8" />
<input type="text" name="products[3].total" id="total4" value="800" />
When you submit this form
the Spring
will populate the list by skipping the elements in the indexes 1
and 2
.
Solutions
Reindex inputs
names with client side JavaScript (for example, after each drop or before sending form
). This is the "cleanest" solution, but it can get tricky, especially when you have nested lists within lists. If you go that way I suggest you structure your markup in a way that facilitates the name change of inputs of a common product (eg, grouping the sets of inputs of a product with a div
) .
Do not remove the inputs. You can use any logic instead of removing inputs from the DOM. For example, you can hide inputs
and include input hidden
to indicate that the attribute has been removed. This is an easy solution, but it wastes a bit of bandwidth and memory as you well observed.
<form:hidden path="products[${status.index}].remove" />
Ajax: You can do the operations dynamically in the background every time the user clicks remove. This is a good solution thinking about keeping atomic operations (each list item is updated individually), but it is important that you understand the trade-offs in terms of usability. When making an Ajax call every time someone clicks remove on the JavaScript side you are anticipating the removal operation before submit
(which may or may not be desirable).
Clear the list before using it on the server side: It's ugly the more it works.
Use a self-organizing collection. I marked an article in references that implements a ShrinkableLazyList
References:
Spring MVC and form binding: how to remove an item from a List?
Dynamic forms, LazyList and transparent items removal