我是MVC的新手并尝试创建向导式的一系列视图,将同一个模型实例从一个视图传递到另一个视图,用户在每个视图上完成更多信息。控制器看起来像这样: -
[HttpGet]
public ActionResult Step1()
{
return View();
}
[HttpPost]
public ActionResult Step1(MyModel model)
{
if (!ModelState.IsValid)
return View(model);
return View("Step2", model);
}
[HttpPost]
public ActionResult Step2(MyModel model)
{
if (!ModelState.IsValid)
return View(model);
return View("Step3", model);
}
// etc..
问题: -
当我从Step1视图提交表单时,它调用Step1 POST方法并导致Step2视图显示在浏览器中。当我在此视图上提交表单时,它再次调用Step1 POST方法!我通过在Html.BeginForm()
中指定操作和控制器名称来实现它,所以我猜测无参数重载只是POST回到渲染视图的操作?
我注意到浏览器的地址栏与当前视图不同步 - 当我在Step2视图中时,它仍显示Step1网址,以及何时开启Step3显示Step2 URL。发生了什么事?
我在视图之间传递模型的另一种方法是将模型放在TempData
中,然后使用RedirectToAction()
。这种方法的优点和缺点与我目前的做法有什么关系?
我不会提供任何"返回"向导中我自己的按钮。关于浏览器的后退按钮是否有任何陷阱需要注意,上述两种方法中的任何一种都有助于(或阻碍)?
修改
@ StephenMuecke的评论提示我现在重写了这个以使用单一视图。我曾经试过这次,但是在绕过一个"步数#34;跟踪我在向导中的位置。我最初使用的是使用@Html.HiddenFor', but this wasn't updating as the underlying model property changed. This appears to be "by design", and the workaround is to create the hidden field using vanilla HTML (
创建的隐藏字段
无论如何,单视图向导现在正在运行。唯一的问题是用户可以在完成向导后单击后退按钮,进行更改,然后重新提交第二次(导致第二个DB记录)的旧栗子。
我尝试在我的POST方法中添加[OutputCache(NoStore = true, Duration = 0, VaryByParam = "None")]
,但所有这一切都显示(在我的情况下)Chrome错误页面,建议用户点击刷新以重新提交表单。这不是用户友好的,也不会阻止第二次提交。
答案 0 :(得分:1)
在这种情况下,您可以使用RedirectToAction()
而无需担心TempData
。只需将模型作为参数添加到每个操作,然后使用RedirectToAction("Step2", model)
;
[HttpGet]
public ActionResult Step1()
{
return View();
}
[HttpPost]
public ActionResult Step1(MyModel model)
{
if (!ModelState.IsValid)
return View(model);
return RedirectToAction("Step2", model);
}
[HttpGet]
public ActionResult Step2(MyModel model)
{
return View(model);
}
[HttpPost]
public ActionResult Step2(MyModel model)
{
if (!ModelState.IsValid)
return View(model);
return RedirectToAction("Step3", model);
}
// etc..
#1的答案可以在#2中找到。如果你没有在Html.BeginForm()
中指定动作,它会发布到当前网址。
使用TempData避免在网址中显示模型。
[HttpGet]
public ActionResult Step1()
{
return View();
}
[HttpPost]
public ActionResult Step1(MyModel model)
{
if (!ModelState.IsValid)
return View(model);
TempData["myModel"] = model;
return RedirectToAction("Step2");
}
[HttpGet]
public ActionResult Step2()
{
var model = TempData["myModel"] as MyModel;
return View(model);
}
[HttpPost]
public ActionResult Step2(MyModel model)
{
if (!ModelState.IsValid)
return View(model);
TempData["myModel"] = model;
return RedirectToAction("Step3");
}
// etc..
另一种选择是将下一个动作的名称添加到ViewBag并在每个BeginForm中设置actionName()
[HttpGet]
public ActionResult Step1()
{
ViewBag.NextStep = "Step1";
return View();
}
[HttpPost]
public ActionResult Step1(MyModel model)
{
if (!ModelState.IsValid)
{
ViewBag.NextStep = "Step1";
return View(model);
}
ViewBag.NextStep = "Step2";
return View("Step2", model);
}
[HttpPost]
public ActionResult Step2(MyModel model)
{
if (!ModelState.IsValid)
{
ViewBag.NextStep = "Step2";
return View(model);
}
ViewBag.NextStep = "Step3";
return View("Step3", model);
}
//View
@using (Html.BeginForm((string)ViewBag.NextStep, "ControllerName"))
{
}
我更喜欢将NextStep作为属性添加到MyModel并使用它而不是使用ViewBag。
答案 1 :(得分:1)
我理解您的方法背后的想法,并没有任何问题。不幸的是,我不相信ASP.NET MVC非常适合在不同的操作之间传递相同的视图模型(包含数据!)。
通常,控制器中的脚手架操作将创建模型项或通过数据库中的标识符查找它。
我不知道这是否会有所帮助,但您可以尝试将其保存到数据库的每一步,然后通过标识符检索它,或者您也可以将其保存到会话中并以此方式抓取它
我看到你的方法的一个问题是你将Step2设置为get,但你可能想要从Step1发布数据而不是使用查询字符串。您可能需要协调该问题。