2

I am new to MVC and JavaScript, so forgive my basic questions. I am writing a view that displays several rows of data and at the end of each row I am displaying a Remove button. When clicked, the button runs some JavaScript that removes the row using the .remove().

When I click the Remove button, the row is removed, but when I return to the controller, any rows below the row that was deleted is not transferred back to the controller and the count is one less then the row I deleted. For example if I have 6 rows and delete row 4, when I submit back to the controller, the count is 3 and rows 5 and 6 are missing. Not sure why. Below is my View and controller.

Any help will be greatly appreciated.

View

@using (Html.BeginForm("Edit", "Contacts", FormMethod.Post))
{
    <table>
        <tr>
            <th><label>Last Name</label></th>
            <th><label>First Name</label></th>
            .... // more table headings
            <th></th>
        </tr>
        @if (Model != null && Model.Count > 0)
        {
            int j = 0;
            foreach (var i in Model)
            {
                <tr>
                    <td>
                        @Html.TextBoxFor(a => a[j].LASTNAME)
                        @Html.HiddenFor(a => a[j].DOB_NBR)
                        @Html.ValidationMessageFor(a => a[j].LASTNAME)
                    </td>
                    <td>
                        @Html.TextBoxFor(a => a[j].FIRSTNAME)
                        @Html.ValidationMessageFor(a => a[j].FIRSTNAME)
                    </td>
                    .... // more controls
                    <td><button type="button" id="btnDelete" class="deleteContact btn btn btn-danger btn-xs">Remove</button></td>
                </tr>
                j++;
            }
        }
    </table>
    <input type="button" class="add-button btn btn-default" name="add" value="Add" />
    <input type="submit" name="submitAction" value="Submit" class="btn btn-primary" onclick="return confirm('Please double check all information before submitting.   Submit Notification?')" />
}
@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
<script type="text/javascript">
    $(document).on("click", ".deleteContact", function () {
        $(this).closest("tr").remove();
    });
</script>

Controller

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(string submitAction, List<ENTITY_CONTACTS> entity_Contacts)
{
    // entity_Contacts is not bound with all items in the collection
}
JBroz
  • 69
  • 1
  • 1
  • 5

1 Answers1

1

By default, the DefaultModelBinder will only bind collections when the indexers are zero-based and consecutive. In your case, when you remove the 4th row, your indexers are 0, 1, 2, 4 and 5 so only the first 3 are bound. You can override this behavior by adding a hidden input for the indexer, which allows the DefaultModelBinder to match up non-consecutive indexers.

Change your loop to

for (int i = 0; i < Model.Count; i++) // do not use a foreach loop
{
    <tr class="contacts-record">
        <td>
            <input type="hidden" name="Index" value="@i" /> // add this
            @Html.TextBoxFor(a => a[i].LASTNAME)
            @Html.HiddenFor(a => a[i].DOB_NBR)
            @Html.ValidationMessageFor(a => a[i].LASTNAME, "", new { @class = "text-danger" })
        </td>
        .... // other td elements
    <tr>
}

For more information on binding to collections, refer Model Binding To A List, and if your also looking to dynamically add items to the collections, refer the answers here and here

Side note: remove the id attribute from your 'Delete' button. Your creating duplicate id attributes which is invalid html. Also remove the scripts for jquery.validate.min.js and jquery.validate.unobtrusive.min.js because you have already added them using @Scripts.Render("~/bundles/jqueryval"), however you need to ensure that jquery-1.10.2.min.js is rendered first, preferably using @Scripts.Render("~/bundles/jquery"). In addition, all the scripts should be inside @section Scripts {

Community
  • 1
  • 1
  • Thanks for the reply that is exactly what I was looking for. I started to create the delete using the .hide(), but your response is much easer. I do have an additional question. Below is my add function. How do I assign a new value to Index? – JBroz Mar 14 '16 at 12:41
  • If you have a new question, then you need to ask a new question, but before you do, I suggest you look at the answers [here](http://stackoverflow.com/questions/29161481/post-a-form-array-without-successful/29161796#29161796) and [here](http://stackoverflow.com/questions/28019793/submit-same-partial-view-called-multiple-times-data-to-controller/28081308#28081308) to understand how to dynamically add and remove collection items - and I just noticed I already had those links in my answer :) –  Mar 14 '16 at 12:44
  • Not only that, but I just realized I gave you those links in your [previous question](http://stackoverflow.com/questions/35919506/dynamically-adding-rows-to-an-html-table-using-javascript) as well where I pointed out that the answer you accepted would not solve your problem –  Mar 14 '16 at 12:48
  • Seriously, you want my help on the other question when you don't even find this correct answer useful? –  Mar 16 '16 at 22:42