【问题标题】:Html.Hidden() is not binding the right values in foreach loopHtml.Hidden() 没有在 foreach 循环中绑定正确的值
【发布时间】:2015-03-25 02:09:27
【问题描述】:

我有一个视图,它使用 foreach 循环来显示带有删除选项的学生列表。但是 Html.hidden 没有按预期获得正确的 student.ID。

这是我的学生班级:

public class Student
{
    public Guid ID { get; set; }
    public string Name { get; set; }
}

我的控制器

public ActionResult Index()
{
    var students = db.Students.OrderBy(s => s.ID).ToList();
    return View(students);
}

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult DeleteStudent(Guid Id)
{
    if (Id != null)
    {
        Student student = db.Students.Single(s => s.ID == Id);
        if (student == null)
        {
            return View("Error");
        }
        db.Students.Remove(student);
        db.SaveChanges();

        return RedirectToAction("Index");
    }
    return View("Error");
}

我的观点

@model IEnumerable<MyProject.Models.Student>
@{ ViewBag.Title = "Student List"; }
<h2>@ViewBag.Title</h2>

<ul>
@foreach (var student in Model)
{
    <li>
    @Html.displayFor(modelItem => student.Name)
    @using (Html.BeginForm("DeleteStudent", "MyController", FormMethod.Post))
    {
        @Html.AntiForgeryToken()
        @Html.Hidden("Id", student.ID)
        <input type="submit" value="Delete" class="btnSubmit">
    }
    </li>
}
</ul>

假设有 3 个学生的 ID 分别为 1、2 和 3。但是当我尝试删除学生 3 时,在控制器上,参数Guid Id 的值为 1,因此 ID = 1 的学生被删除。

实际上,无论我删除哪个学生,Id 总是取列表中第一个学生的值。

我做错了什么,或者有什么更好的解决方法?

【问题讨论】:

  • 您的模型使用@model MyProject.Models.Students,而控制器通过Students.ToList()。我猜这是不匹配。试试@model MyProject.Models.Students[]
  • 感谢您指出这一点!我在我的问题中添加了IEnumerable。它现在可以正常工作而无需更改为TrueBlueAussie's Answer

标签: asp.net asp.net-mvc razor


【解决方案1】:

修改后的答案

重新审视您的要求,@Html 助手并没有真正给您带来任何好处。您的基本设置是生成大量表单,每个表单都旨在删除一个项目。最低要求是某种识别项目的方式。目前的问题是@Html.Hidden同时生成name="ID"id="ID",并且不允许重复ID,无法正确处理。

我可以想出几种方法来解决这个问题:

  1. 将 id 作为参数传递给表单本身(我的首选解决方案)

例如

@using (Html.BeginForm("DeleteStudent", "MyController", FormMethod.Post, new {id=student.ID})){

控制器只接受一个 id 值作为参数。这意味着您不需要隐藏字段。

  1. 通过手动生成的隐藏字段提供单个隐藏值

例如

<input name="ID" value="@student.ID" type="hidden"/>

控制器只接受一个 id 值作为参数。

  1. 如下所述使用@Html.HiddenFor,但控制器操作采用通用FormCollection 参数,并且您拉出单独命名的元素(现在可能是最糟糕的选择)。

原答案

通常,尽可能使用@Html.xxxFor 方法。然后他们为回发正确命名项目。

作为第二条规则,您不能使用foreach 查找来呈现@Html.xxxFors,因为这些方法实际上会解释表达式并查找索引值。 foreach 不提供索引值,因此您必须改用简单的for 循环,以便表达式看起来像Model[i].Property

例如

for (int i = 0; i < Model.length; i++)
{
    @Html.EditorFor(x=>Model[i].Name)
}

表达式中存在的索引导致输入字段中出现name="Name[0]"name="Name[1]" 等。

回到手头的问题:您希望每个表单都有一个隐藏字段,因此您应该像这样使用for 循环和@Html.HiddenFor

for (int i = 0; i < Model.length; i++)
{
    @using (Html.BeginForm("DeleteStudent", "MyController", FormMethod.Post))
    {
        @Html.AntiForgeryToken()
        @Html.HiddenFor(x=>Model[i].ID)
        <input type="submit" value="Delete" class="btnSubmit">
    }
}

另外,正如评论中提到的,您的观点 model 似乎是错误的。通常您会使用IEnumerableIList 之类的:

@model IEnumerable<MyProject.Models.Student>

【讨论】:

  • 感谢您的解决方案! HiddenFor 需要是 lambda 表达式,那么它会是 @Html.HiddenFor(modelItem =&gt; Model[i].ID) 吗?我应该将控制器参数更改为什么 - 类似于DeleteStudent(Student postbackstudent)
  • 这是一个剪切和粘贴错误(第一个示例是正确的,已修复第二个示例)。控制器方法可能需要更通用的FormCollection,因为在此示例中传回的 ID 会发生变化。我需要再次查看您的原始问题,因为仅重复名称是有效的,只要您也不要重复 ID。即使用手册 &lt;input type="hidden" name="id" value="@student.Id"/&gt; 而不是 HTML 帮助器。
  • @Ramesisiii:为您添加了几个选项。向表单添加路由可能是最简单的解决方案,但选项 1 或 2 都不错。
  • 感谢您结束与Html.Hidden()的战斗!
【解决方案2】:

我怀疑您总是获得相同 GUID 的原因是因为所有隐藏的输入控件都具有相同的 ID。

如果内存服务,当 MVC 尝试将参数映射到表单元素时,它将匹配第一个字段、查询字符串变量等,匹配参数名称(不区分大小写)。

您的表单有多个隐藏的输入控件,都命名为“ID”。只有第一个将映射到您的 DeleteStudent 操作的 ID 参数。其余的可能会被忽略。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-11-24
    • 2015-07-04
    • 1970-01-01
    • 2011-12-10
    • 2021-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多