erictrigo
erictrigo

Reputation: 1017

Incorrect model value within foreach in my view

I have a table where I keep a list of Roles, which has three columns: Name and two buttons Edit and Delete. When users click on Edit, it takes them to another form where they can edit the name of the role. If they click on Delete, they get a modal dialog asking them for verification before they delete the role.

My issue is the following: if I click on the Edit button, it detects the right Role and the correct role name shows up in the new form that appears. However, if I click on Delete, it always grabs the first Role of the Model list (that is, the first role of the table), no matter which one I clicked on. Why?

Here's my view:

<table class="table table-striped table-hover ">
    <thead>
        <tr>
            <th>@Resources.Role</th>
            <th></th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var role in Model)
        {
            <tr>
                <td>
                    @role.Name
                </td>
                <td>
                    @using (Html.BeginForm("RoleEdit", "Admin", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Get, new { @class = "form-horizontal", role = "form" }))
                    {
                        @Html.AntiForgeryToken()
                        @Html.HiddenFor(m => m.Where(r => r.Id.Equals(role.Id)).FirstOrDefault().Name)
                        <input type="submit" value="@Resources.Edit" class="btn btn-default btn-sm" />
                    }
                </td>
                <td>
                    <input type="submit" value="@Resources.Delete" class="btn btn-default btn-sm" data-toggle="modal" data-target="#confirm-delete" />

                    <div class="modal fade" id="confirm-delete" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
                        <div class="modal-dialog">
                            <div class="modal-content">
                                <div class="modal-header">
                                    @Resources.DeleteRole
                                </div>
                                <div class="modal-body">
                                    @Resources.AreYouSureYouWantToDelete
                                    <hr />
                                    @role.Name
                                </div>
                                <div class="modal-footer">
                                    @using (Html.BeginForm("RoleDelete", "Admin", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
                                    {
                                        @Html.AntiForgeryToken()
                                        @Html.HiddenFor(m => m.Where(r => r.Id.Equals(role.Id)).FirstOrDefault().Name)
                                        <button type="button" class="btn btn-default" data-dismiss="modal">@Resources.Cancel</button>
                                        <input type="submit" value="@Resources.Delete" class="btn btn-danger btn-ok" />
                                    }
                                </div>
                            </div>
                        </div>
                    </div>
                </td>
            </tr>
        }
    </tbody>
</table>

Upvotes: 0

Views: 573

Answers (1)

user3559349
user3559349

Reputation:

Your issue is that you are creating duplicate id attributes in your foreach loop (which is invalid html) in the following line of code

The submit button above it is target the element with that id

<input type="submit" ... data-target="#confirm-delete" />

which will only ever return the first element with id="confirm-delete"

One way to solve this if to ensure your elements have unique id attributes, for example

@{ int counter = 0; }
@foreach (var role in Model)
{
  var id = string.Format("confirm-delete-{0}", ++counter);
  var target = string.Format("#{0}", id);
  ....
  <input type="submit" ... data-target="@target" />
  <div class="modal fade" id="@id" ...>

However this design is generating a separate dialog in each iteration and generating a lot of extra unnecessary html. It would be be better to have one dialog and handle the .submit() event to display the dialog and cancel the submit if it returns false. You have not indicated which jquery plugin your using for the dialog, so that will need to be the subject of a separate question.

Side notes:

You can simplify your view by using a link for the redirecting to the Edit() method rather than using a form element

@Html.ActionLink("Edit", "RoleEdit", "Admin", new { ID = role.Id }, new { @class = "btn btn-default btn-sm" })

and int the 'Delete' form, you can add the Id as a route value and omit the hidden input. Note also that your deleting data so it should be a POST, not a GET

@using (Html.BeginForm("RoleEdit", "Admin", new { ReturnUrl = ViewBag.ReturnUrl, id = item.Id }, FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
    @Html.AntiForgeryToken()
    <input type="submit" value="@Resources.Edit" class="btn btn-default btn-sm" />
}

however a better way to handle the 'Delete' would be to post the value using ajax and return a result indicating success or otherwise, and if successful, remove the corresponding item form the DOM. This allows the users to stay on the same page and continue to delete other items.

Upvotes: 1

Related Questions