我有一个视图,用户可以使用HTML表单记录在Activity上花费的时间。因此,该视图循环遍历所有活动的列表,并为每个活动生成一个日志时间表(包含在_LogTime部分视图中)。从Index视图传递到局部视图的唯一信息是ActivityId,它以隐藏的形式放置。其余所需信息由用户通过from提供。
我遇到的问题是,一旦我提交了其中一个表单,所有表单的隐藏字段都将设置为我刚刚提交的表单的ActivityId。值得注意的是,当页面首次加载时(在我提交任何表单之前),隐藏字段是正确的,当我第一次提交表单时,正确的活动会将时间记录到它(并且没有其他错误地获取时间记录)。但之后的任何表单提交只会记录我第一次提交表单的活动的时间。
知道这里发生了什么吗?为什么所有隐藏字段都设置为相同的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());
}
}
答案 0 :(得分:2)
您遇到的是由于html帮助器方法自动更新具有相同名称的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方法可以指示应用程序流程的更深层次问题。我同意Erik的观点。正如他指出的那样,重新设计应用程序的流程可能是一项耗时的任务。
当遇到问题所指示的行为时,如果有办法在不修改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());
}
答案 1 :(得分:0)
我从不在Controller中使用全局变量。 我宁愿把所有隐藏的值,也就是那些在foreach局部视图中的值放在表单中。
这样,您传递整个列表并在此之后添加一个。 现在我认为你传递一个空行并将最后一行添加到那里。
可以肯定的是,你可以在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>
}