Reputation: 1591
I have a table where I add and remove rows dynamically:
@model AHBReports.Models.AdjustmentModel
@using (Html.BeginForm())
{
<table id="container">
@Html.EditorFor(model => model.Adjustments)
</table>
<div >
<input type="button" id="btnAdd" value="+ Add" />
<input type="submit" value="Submit" />
</div>
}
EditorTemplate:
@model AHBReports.Models.Adjustment
<tr>
<td>@Html.HiddenFor(x => x.Id, new { @class = "iHidden" })</td>
<td>@Html.AutocompleteFor(model => model.BRANCH, "GetBranches", "Report700H")</td>
<td>@Html.EditorFor(model => model.Amount)</td>
<td><a onclick="removeRow(this)">x</a></td>
</tr>
Script for table manipulation:
<script type="text/javascript">
function removeRow(selector)
{
if ($('#container tr').length > 1)
{
$(selector).closest('tr').remove();
}
}
$(document).ready(function () {
$("#btnAdd").click(function (e) {
var ind = $("#container tr:last").find("input.iHidden").val();
var itemIndex = parseInt(ind);
itemIndex++;
console.log(itemIndex);
e.preventDefault();
var newItem = $("<tr>"+
"<td><input id='Adjustments_" + itemIndex + "__Id' type='hidden' value='"+itemIndex+"' class='iHidden' name='Adjustments[" + itemIndex + "].Id' /></td>" +
"<td><input type='text' id='Adjustments_" + itemIndex + "__BRANCH' name='Adjustments[" + itemIndex + "].BRANCH' data-autocomplete-url='@Url.Action("GetBranches", "Report700H")'/></td>" +
"<td><input type='text' id='Adjustments_" + itemIndex + "__Amount' name='Adjustments[" + itemIndex + "].Amount'/></td>" +
"<td><a onclick='removeRow(this)'>x</a></td>" +
"</tr>");
$("#container").append(newItem);
});
});
My add/delete functions work fine visually in my view, as well as when I accept the collection in my POST method:
public ActionResult Adjust(AdjustmentModel model)
{
//working with model.Adjustments
}
I receive correct values. However, when I try to delete some row, which is in the middle of the table and then sumbit the form, I receive only elements that were above deleted row, for example:
id branch amount
0 aaa 500
1 bbb 200
2 ccc 300 --deleted this row
3 ddd 400
Collection receives:
id branch amount
0 aaa 500
1 bbb 200
So, the last row is missing. What am I doing wrong??
Thanks a lot
Upvotes: 3
Views: 7144
Reputation: 8726
When the row you deleted it containts model's input and input has name and id based on index.
So when you delete row you have to update input's name and id that are in row after the deleted row.. or regenerate row next all from deleted row with new index name .
Replace your delete function with this one
function removeRow(selector) {
if ($('#container tr').length > 1) {
$(selector).closest('tr').remove();
var itemIndex =0;
$('#container tr').each(function(){
var this_row = $(this);
this_row.find('input[name$=".BRANCH"]').attr('name','Adjustments[' + itemIndex + '].BRANCH');//replace name of input that ends the name BRANCH
this_row.find('input[name$=".Amount"]').attr('name','Adjustments[' + itemIndex + '].Amount');
this_row.find('input[name$=".Id"]').attr('name', 'Adjustments[' + itemIndex + '].Id');
itemIndex ++;
});
}
}
Upvotes: 5
Reputation:
The indexer for collections must start at zero and be consecutive unless you use an Index
property where the value of Index
is equal to the indexer. For example
<input ... name="Adjustments[0].ID" ..>
<input ... name="Adjustments[2].ID" ..>
wont post back correctly. But if you add an Index property for the object
<input ... name="Adjustments[0].ID" ..>
<input ... name="Adjustments[0].Branch" ..>
<input ... name="Adjustments[0].Index" value="0"..>
<input ... name="Adjustments[2].ID" ..>
<input ... name="Adjustments[2].Branch" ..>
<input ... name="Adjustments[2].Index" value="2"..>
Then the collection will post back correctly
Since you don't have access to the indexer in the EditorTemplate
, you will need to generate the controls in a for loop
in the main page
for (int i = 0; i < Model.Adjustments.Count; i++)
{
var name = string.Format("Adjustments[{0}].Index", i);
@Html.HiddenFor(m => m[i].ID)
....
<input type=hidden name="@name" value="@i" />
}
You will also need to modify your script to include the hidden input for the Index
property. Rather than basing the value of itemIndex
on the number of existing rows, base it on a unique value. For example
$("#btnAdd").click(function (e) {
var itemIndex = (new Date()).getTime();
Upvotes: 2
Reputation: 730
u can give the row an unique id.
var newItem = $("<tr id='row"+itemIndex+"'>"+
"<td><input id='Adjustments_" + itemIndex + "__Id' type='hidden' value='"+itemIndex+"' class='iHidden' name='Adjustments[" + itemIndex + "].Id' /></td>" +
"<td><input type='text' id='Adjustments_" + itemIndex + "__BRANCH' name='Adjustments[" + itemIndex + "].BRANCH' data-autocomplete-url='@Url.Action("GetBranches", "Report700H")'/></td>" +
"<td><input type='text' id='Adjustments_" + itemIndex + "__Amount' name='Adjustments[" + itemIndex + "].Amount'/></td>" +
"<td><a onclick='$('#row"+ itemIndex +").remove();'>x</a></td>" +
"</tr>');
Actually this works fine for a smiliar page which i've created.
Greetings
Upvotes: 0