Gyuzal
Gyuzal

Reputation: 1591

delete table row dynamically using jQuery in asp.net mvc

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

Answers (3)

sangram parmar
sangram parmar

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

user3559349
user3559349

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

Bernd
Bernd

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

Related Questions