【问题标题】:Basic wizard, and passing model from one view to another?基本向导,并将模型从一个视图传递到另一个视图?
【发布时间】:2015-08-06 14:38:08
【问题描述】:

我是 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..

问题:-

  1. 当我从 Step1 视图提交表单时,它调用 Step1 POST 方法并导致 Step2 视图显示在浏览器中。当我在 this 视图上提交表单时,它再次调用 Step1 POST 方法!我通过在Html.BeginForm() 中指定动作和控制器名称来让它工作,所以我猜测无参数重载只是返回到呈现视图的动作?

  2. 我注意到浏览器的地址栏与当前视图不同步 - 当我在 Step2 视图上时,它仍然显示 Step1 URL,而在 Step3 时它显示 Step2 URL。怎么回事?

  3. 我见过的另一种在视图之间传递模型的方法是将模型放入TempData,然后使用RedirectToAction()。与我目前正在做的相比,这种方法的优缺点是什么?

  4. 我不会在向导中提供任何我自己的“后退”按钮。关于浏览器的后退按钮,是否有任何需要注意的陷阱?以上两种方法中的任何一种是否有帮助(或阻碍)?

编辑

在@StephenMuecke 的评论的提示下,我现在已将其重写为使用单个视图。我之前尝试过一次,但在往返“步数”以跟踪我在向导中的位置时遇到了困难。我最初使用的是使用@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 ( 创建的隐藏字段

无论如何,单视图向导现在正在工作。唯一的问题是用户在完成向导后能够单击返回按钮、进行更改并重新提交第二次(导致第二条数据库记录)。

我尝试将[OutputCache(NoStore = true, Duration = 0, VaryByParam = "None")] 添加到我的 POST 方法中,但所做的只是显示(在我的情况下)一个 Chrome 错误页面,建议用户单击刷新以重新提交表单。这对用户不友好,也不会阻止第二次提交。

【问题讨论】:

  • 您为什么不使用单个表单来显示/隐藏每个步骤?
  • @StephenMuecke 谢谢,看看我的编辑!
  • 我的想法更像是this answer

标签: asp.net-mvc


【解决方案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() 中指定 Action,它会发布到当前 url。

使用 TempData 避免模型在 url 中显示。

[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。

【讨论】:

  • 刚刚尝试了RedirectToAction 重载,我注意到这导致模型属性在 URL 中显示为查询字符串参数。在我的场景中这没问题,但我想如果模型特别大,这可能是个问题?
  • true.. 我会给你其他选择。
  • 不要为此使用TempData。如果用户在向导过程中刷新了任何页面,整个事情都会失败。并避免将模型传递给 GET 方法。如果模型包含复杂对象或集合的属性,绑定将失败,您很容易超过查询字符串限制并抛出异常。
  • 您只需将@{ TempData["myModel"] = Model; } 添加到您的视图中,以防止刷新导致问题。
【解决方案2】:

我了解您的方法背后的想法,并且对此没有任何问题。不幸的是,我不认为 ASP.NET MVC 非常适合在不同操作之间传递相同的视图模型(带有数据!)。

通常,控制器中的脚手架操作将创建模型项或通过数据库中的标识符找到它。

我不知道这是否会有所帮助,但您可以尝试在每一步将其保存到数据库中,然后通过标识符检索它,或者您也可以将其保存到会话中并以这种方式获取。

我在您的方法中看到的一个问题是您将 Step2 设置为 get,但您可能希望从 Step1 向其发布数据,而不是使用查询字符串。您可能需要协调该问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-18
    • 2014-04-03
    • 1970-01-01
    • 2013-03-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多