首页 > 解决方案 > 使用模型绑定和 POST 请求在每个表行上实现删除按钮

问题描述

在我看来,我有一个表格,我想用它来删除特定的行。我使用一个 foreach 循环来生成一个隐藏的输入字段,其中包含我想要传递给控制器​​和asp-for标签以进行模型投标的行值,以及一个提交按钮。

传递给控制器​​的值始终是第一行。我倾向于认为这种行为的原因是生成的输入字段都具有相同的 name 属性,因为asp-for表达式对于 foreach 循环的每次迭代都是不变的。

有没有一种直接的方法可以使用表单和 POST 请求来实现这一点,或者我应该只使用带有路由值的锚点,即 GET 请求?

这是我的视图模型:

public class RolesViewModel
}
    public IList<AppUser> UsersInRole {get; set;}
    public string SelectedRole {get; set;}
    public RemoveUserFromRole RemoveUser {get; set;}

    public class RemoveUserFromRole 
    {
        public string UserName {get; set;}
        public string RoleName {get; set;}
    }
}

我的观点

<form method="post" asp-action="RemoveUser" id="removeUserForm"></form>
<table id="userTable" class="table table-striped table-sm">
    <thead>
        <tr>
            <th scope="col">User name</th>
            <th scope="col" class="text-center">Delete</th>
        </tr>
    </thead>
    <tbody>
    @foreach (var user in Model.UsersInRole)
    {
        <tr>
            <td>@user.UserName</td>
            <td class="text-center">
                <input form="removeUserForm" asp-for="RemoveUser.UserName" type="hidden" value="@user.UserName" />
                <input form="removeUserForm" asp-for="RemoveUser.RoleName" type="hidden" value="@Model.SelectedRoleName" />
                <button form="removeUserForm" type="submit" class="btn btn-sm btn-link text-danger py-0 my-0">
                    <i class="fas fa-times"></i>
                </button>
            </td>
        </tr>
    }
    </tbody>
</table>

我在控制器中的操作方法

[HttpPost]
public async Task<IActionResult> RemoveUser(RolesViewModel model)
{
    //model.RemoveUser.UserName always have the value from the first row
    var user = await _userManager.FindByNameAsync(model.RemoveUser.UserName);
    if (user == null)
        return RolesError(await GetModel());

    var result = await _userManager.RemoveFromRoleAsync(user, model.RemoveUser.RoleName);
    if (!result.Succeeded)
        return RolesError(await GetModel());


    return RedirectToAction("Roles", new { roleName = model.RemoveUser.RoleName });
}

在此先感谢您的时间。

标签: c#asp.net-mvcasp.net-core

解决方案


根据您的代码,我发现您有多个隐藏文件,其中包含user.UserName.

如果你点击提交按钮,它会将所有隐藏的字段值上传到代码隐藏中,它只会绑定第一个,这就是你的模型总是第一个的原因。

您可以在 F12 开发工具的网络中找到 formdata。

在此处输入图像描述

为了解决这个问题,我们有一个简单但不是很好的解决方案。

我们可以在您的表格中设置多个表单标签,以避免将所有隐藏的用户名值发布到控制器:

如下所示:

<table id="userTable" class="table table-striped table-sm">
    <thead>
        <tr>
            <th scope="col">User name</th>
            <th scope="col" class="text-center">Delete</th>
        </tr>
    </thead>

    <tbody>

         @foreach (var user in Model.UsersInRole)
         { int i = 0;



            <tr>

                <td>@user.UserName</td>
                <td class="text-center">
        <form method="post" asp-action="RemoveUser" id="@user.UserName">

                    <input form="@user.UserName" name="RemoveUser.UserName" type="hidden" value="@user.UserName" />
                    <input form="@user.UserName" name="RemoveUser.RoleName" type="hidden" value="@Model.SelectedRole" />
                    <button form="@user.UserName" type="submit" class="btn btn-sm btn-link text-danger py-0 my-0">
                        <i class="fas fa-times">iiiii</i>
                    </button>
        </form>
                </td>

            </tr>

                }
    
        </tbody>
</table>

如果您选择这种方式,您应该重建所有视图的 html 构成。

此外,您可以尝试使用 ajax 来实现您的要求,这个解决方案比以前的解决方案要好。您可以使用 jquery 根据提交按钮的 id 或位置获取正确的表单数据,然后使用 jquery ajax 将表单数据发布到控制器中。然后你可以返回重定向 url 而不是RedirectToAction方法。

更多关于如何使用 ajax 发送表单数据的详细信息,您可以参考以下代码:

@model MVCRelatedIssue.Models.RolesViewModel
@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<form method="post" asp-action="RemoveUser" id="removeUserForm">

    <table id="userTable" class="table table-striped table-sm">
        <thead>
            <tr>
                <th scope="col">User name</th>
                <th scope="col" class="text-center">Delete</th>
            </tr>
        </thead>

        <tbody>

            @foreach (var user in Model.UsersInRole)
            {
                <tr>

                    <td>@user.UserName</td>
                    <td class="text-center">
                        <input form="removeUserForm" name="RemoveUser.UserName" type="hidden" value="@user.UserName" />
                        <input form="removeUserForm" name="RemoveUser.RoleName" type="hidden" value="@Model.SelectedRole" />
                        <button form="removeUserForm" type="submit" id="submit" class="btn btn-sm btn-link text-danger py-0 my-0 subbtn">
                            <i class="fas fa-times">iiiii</i>
                        </button>
                    </td>

                </tr>

            }

        </tbody>
    </table>
</form>

@section Scripts{
    <script>
        $(document).ready(function () {
            $(".subbtn").bind("click", function (e) {
                e.preventDefault();
                var formdata = new FormData();
                var UserName = $(this).prev().prev().val();
                formdata.append("RemoveUser.UserName", UserName);
                console.log(UserName);
                var roleName = $(this).prev().val();
                formdata.append("RemoveUser.RoleName", roleName);
                console.log(roleName);

                $.ajax({
                    type: "POST",
                    url: "/RemoveUser/RemoveUser",
                    data: formdata,
                    contentType: false,
                    processData: false,
                    success: function (data) {
                        alert("success");
        
                        window.location.href = data;
                    }
                });

             });
        }); 
    </script>
}

控制器:

    [HttpPost]
    public async Task<IActionResult> RemoveUser(RolesViewModel model)
    {
        //model.RemoveUser.UserName always have the value from the first row
        //var user = await _userManager.FindByNameAsync(model.RemoveUser.UserName);
        //if (user == null)
        //    return RolesError(await GetModel());

        //var result = await _userManager.RemoveFromRoleAsync(user, model.RemoveUser.RoleName);
        //if (!result.Succeeded)
        //    return RolesError(await GetModel());

         string redirecturl = "/RemoveUser/Roles?roleName=" + model.RemoveUser.RoleName;

        return Ok(redirecturl);
    }

结果:

在此处输入图像描述


推荐阅读