隐藏字段在第一次POST后具有错误的值

本文关键字:有错误 POST 字段 第一次 隐藏 | 更新日期: 2023-09-27 18:04:56

我有一个视图,用户可以使用HTML表单记录花费在活动上的时间。因此,该视图循环遍历所有活动的列表,并为每个活动生成一个日志时间表单(包含在_LogTime部分视图中)。从Index视图传递给分部视图的唯一信息是ActivityId,它被放置在一个隐藏的表单中。其余所需信息由用户通过from提供。

我遇到的问题是,一旦我提交了一个表单,所有表单的隐藏字段被设置为我刚刚提交的表单的ActivityId。值得注意的是,当页面第一次加载时(在我提交任何表单之前),隐藏的字段是正确的,当我第一次提交表单时,正确的Activity会被记录时间(其他的都不会被错误地记录时间)。但是在此之后的任何表单提交将只记录我第一次提交表单的活动的时间。

你知道这是怎么回事吗?为什么所有的隐藏字段都被设置为相同的ActivityId?为什么只在第一次POST之后?

如果你需要澄清问题,请告诉我。

模型:

public class Activity
{
    public int ActivityId { get; set; }
    public string Name { get; set; }
}
public class UserActivity
{
    public int UserId { get; set; }
    public int ActivityId { get; set; }
    public int Duration { get; set; }
    public DateTime Date { get; set; }
}

视图:

// Index View
@foreach (Activity activity in Model)
{
    @Html.Partial("_LogTime", new UserActivity(activity.ActivityId))
}
// _LogTime Partial View
@using (Html.BeginForm())
{
    <fieldset>
        @Html.HiddenFor(model => model.ActivityId)
        @Html.EditorFor(model => model.Duration)
        @Html.EditorFor(model => model.Date)
        <input type="submit" value="LOG TIME" />
    </fieldset>
}

控制器:

public class ActivityController : Controller
{
    private readonly DbContext _db = new DbContext();
    public ActionResult Index()
    {
        return View(_db.Activities.ToList());
    }
    [HttpPost]
    public ActionResult Index(UserActivity activity)
    {
        if (ModelState.IsValid)
        {
            _db.UserActivities.Add(activity);
            _db.SaveChanges();
        }
        return View(_db.Activities.ToList());
    }
}

隐藏字段在第一次POST后具有错误的值

您所经历的是由于html helper方法自动更新表单元素与同名的post变量。这些值存储在ModelState中。解决这个问题的一种方法是从ModelState中删除有问题的条目。

另一个可能的修复方法是重定向。

[HttpPost]
public ActionResult Index(UserActivity activity)
{
    if (ModelState.IsValid)
    {
        _db.UserActivities.Add(activity);
        _db.SaveChanges();
    }
    // Remove the ActivityId from your ModelState before returning the View.
    ModelState.Remove("ActivityId")
    return View(_db.Activities.ToList());
}

如下面的注释所示,使用Remove方法可能表明应用程序流存在更深层次的问题。在这一点上,我同意埃里克的观点。正如他所指出的,重新设计应用程序的流程可能是一项耗时的任务。

当遇到问题所指示的行为时,如果有不修改ModelState就能解决问题的方法,那将是首选的解决方案。一个恰当的例子可能是多个元素受到此问题的影响。

为了完整起见,这里有一个替代解决方案:

[HttpPost]
public ActionResult Index(UserActivity activity)
{
    if (ModelState.IsValid)
    {
        _db.UserActivities.Add(activity);
        _db.SaveChanges();
    }
    return RedirectToAction("Index");
}

为了让我的批评者闭嘴,这里是他无法想出的重写。

// Index View
@using (Html.BeginForm())
{
    @for (var i = 0; i < Model.Count; i++)
    {
        <div>
            @Html.HiddenFor(model => model[i].ActivityId)
            @Html.EditorFor(model => model[i].Duration)
            @Html.EditorFor(model => model[i].Date)
        </div>
    }
    <input type="submit" value="LOG TIME ENTRIES" />
}
// Controller Post Method
[HttpPost]
public ActionResult Index(List<UserActivity> activities)
{
    if (ModelState.IsValid)
    {
        foreach( var activity in activities ) 
        {
            var first = _db.UserActivities
                  .FirstOrDefault(row => row.ActivityId == activity.ActivityId );
            if ( first == null ) {
               _db.UserActivities.Add(activity);
            } else {
               first.Duration = activity.Duration;
               first.Date = activity.Date;
            }
        }
        _db.SaveChanges();
        return RedirectToAction("index");
    }
    // when the ModelState is invalid, we want to 
    // retain posted values and display errors.
    return View(_db.Activities.ToList());
}

我从不在控制器中使用全局变量。我宁愿把所有隐藏的值,也包括那些在foreach partial视图中的值,放在。

这样,传递整个列表并在后面添加一个。现在我认为你传递了一个空行,并将最后一个添加到其中。

当然,您可以在post函数中放置一个断点。

@using (Html.BeginForm())
{
    // Index View
    @foreach (Activity activity in Model)
    {
        @Html.Partial("_LogTime", new UserActivity(activity.ActivityId))
    }
    // _LogTime Partial View
    <fieldset>
        @Html.HiddenFor(model => model.ActivityId)
        @Html.EditorFor(model => model.Duration)
        @Html.EditorFor(model => model.Date)
        <input type="submit" value="LOG TIME" />
    </fieldset>
}