【问题标题】:Passing multiple models from one view to a controller将多个模型从一个视图传递到控制器
【发布时间】:2014-06-12 21:32:09
【问题描述】:

如果我在谷歌上搜索“一个视图中的多个模型”,我只能找到有关如何将模型传递给视图的结果。但我对“从视图到控制器”方向感兴趣。

让我们假设:

  • 我在一个视图中有 3 个不同的表单和 1 个表格 (WebGrid)。
  • 每个表格有一个模型,桌子有一个模型。
  • 让我的模型类为ModelF1ModelF2ModelF3ModelT

到目前为止,我看到的所有示例都使用容器 ViewModel,例如

class MyViewModel {
    ModelF1 inst1,
    ModelF2 inst2,
    ModelF3 inst3,
    ModelT instT
}

然后他们以两种方式在视图 控制器之间传递它。

但我想在不使用视图模型的情况下以这种方式捕捉我的模型:

class MyController {
    ActionResult Index() {
        return new View(modelF1Instance, modelF2Instance, modelF3Instance, modelTInstance);
    }

    ActionResult Form1Action(ModelF1 inst1, ModelT instT) {
        // process models after a form1 submit
    }

    ActionResult Form2Action(ModelF2 inst2, ModelT instT) {
        // process models after a form2 submit
    }

    ActionResult Form3Action(ModelF3 inst3, ModelT instT) {
        // process models after a form3 submit
    }
}

如果不解析 CustomBinder 中的整个表单元素,这是否可行?

【问题讨论】:

标签: asp.net .net asp.net-mvc asp.net-mvc-4 asp.net-mvc-5


【解决方案1】:

首先,您只能使用将强类型视图模型发送回您的视图

return View(model);

View 是基类上的一个方法,而不是用return new View(... 实例化的类

然后是你真正的问题:是的,你可以这样做,但是使用包含不同表单项的顶级 ViewModel 在大多数用例中要容易得多强>。顶级容器 ViewModel 处理得非常好的主要问题是值持久性和服务器端验证以及往返之间的错误消息。

如果您只是担心创建顶级 ViewModel 容器会导致效率低下,请不要担心。这比您可能必须采取的所有解决方法要有效得多,以便在没有顶级 ViewModel 的情况下使行为良好的表单工作。

下面有一些示例代码。下面的代码应该证明使用包含在顶级 ViewModel 中的模型更加简单和整洁:一些表单故意不往返某些状态。请注意HiddenForModelState.Clear 的用法,它们都与您要执行的操作相关,但即使这些也不会保留inst4.Name 的值以用于Form4Submit。探索的各种选项是:

  1. 使用查询参数来表示正在发布的表单
  2. 使用不同的表单名称,但仍使用视图模型。
  3. 对表单使用仅重定向操作(发送新实例,并且仅发送部分视图模型)
  4. 混合使用上述方法
public class TestController : Controller
{
    //
    // GET: /Test/
    [System.Web.Mvc.HttpGet]
    public ActionResult Index(string msg = null)
    {
        var model = new MyViewModel
        {
            Inst1 = new ModelF1 { Name = "Name of F1" },
            Inst2 = new ModelF2 (),
            InstT = new ModelT {Name = "Name of T"},
            PostNumber = 0,
            Message = msg
        };
        return View(model);
    }

    [System.Web.Mvc.HttpPost]
    public ActionResult Index(MyViewModel model, int option = 1)
    {
        // process models after a form1/2 submit
        model.Message = "You posted " + 
            ((option == 1) ? model.Inst1.Name : model.Inst2.Name)
            + " to Index for "
            + ((option == 1) ? "inst1" : "inst2");
        model.PostNumber ++;
        //  This, and the hiddenFor are required to allow us to update the PostNumber each time
        ModelState.Clear();
        return View(model);
    }

    [System.Web.Mvc.HttpPost]
    public ActionResult Form2Submit(MyViewModel model)
    {
        // process models after a form2 submit
        model.Message = "You posted " + model.Inst2.Name + " to Form2Submit";
        model.PostNumber++;
        ModelState.Clear();
        return View("Index", model);
    }

    [System.Web.Mvc.HttpPost]
    public ActionResult Form3Submit(ModelF3 inst3, ModelT instT)
    {
        // process models after a form3 submit
        var n = instT.Name;
        var msg = "You posted " + inst3.Name + ", " + n + " to Form3Submit";
        //  We no longer have access to pass information back to the view, so lets redirect
        return RedirectToAction("Index", new { msg = msg });
    }

    [System.Web.Mvc.HttpPost]
    public ActionResult Form4Submit(ModelF4 inst4, MyViewModel model)
    {
        // process models after a form4 submit
        var n = model.InstT.Name;
        model.Message = "You posted " + inst4.Name + ", " + n + " to Form4Submit";
        model.PostNumber++;
        ModelState.Clear();
        return View("Index", model);
    }

    public class MyViewModel
    {
        public int PostNumber { get; set; }
        public string Message { get; set; }
        public ModelF1 Inst1 { get; set; }
        public ModelF2 Inst2 { get; set; }
        public ModelT InstT { get; set; }
    }

    public class ModelBase { public string Name { get; set; } }

    public class ModelF1 : ModelBase {}
    public class ModelF2 : ModelBase { }
    public class ModelF3 : ModelBase { }
    public class ModelF4 : ModelBase { }
    public class ModelT : ModelBase { }
}

那么对于多表单视图:

@using MyWebSite.Controllers;
@model TestController.MyViewModel
@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
    <body>
        <p>
            @Html.Raw(Model.PostNumber) : @Html.Raw(Model.Message)
        </p>
        <p>
            @Html.LabelFor(m => Model.InstT) : <br />
            @Html.DisplayFor(m => Model.InstT)
        </p> 
        <div>
            <p>Default form submit</p>
            @using (Html.BeginForm())
            {
                <div>
                    @Html.HiddenFor(m => m.PostNumber)
                    @Html.LabelFor(m => Model.Inst1.Name)
                    @Html.TextBoxFor(m => Model.Inst1.Name)
                </div>
                <input type="submit" value="Submit Index" />
            }
        </div>
        <div>
            <p>Use a parameter to denote the form being posted</p>
            @using (Html.BeginForm("Index", "Test", new { option = 2 }))
            {
                <div>
                    @* Omitting these will not persist them between trips
                    @Html.HiddenFor(m => Model.Inst1.Name)
                    @Html.HiddenFor(m => Model.InstT.Name)*@
                    @Html.HiddenFor(m => m.PostNumber)
                    @Html.LabelFor(m => Model.Inst2.Name)
                    @Html.TextBoxFor(m => Model.Inst2.Name)
                </div>
                <input type="submit" value="Submit with option parameter" />
            }
        </div>
        <div>
            <p>Use a different form name, but still use the ViewModel</p>
            @using (Html.BeginForm("Form2Submit", "Test"))
            {
                <div>
                    @Html.HiddenFor(m => Model.Inst1.Name)
                    @Html.HiddenFor(m => Model.InstT.Name)
                    @Html.HiddenFor(m => m.PostNumber)
                    @Html.LabelFor(m => Model.Inst2.Name)
                    @Html.TextBoxFor(m => Model.Inst2.Name)
                </div>
                <input type="submit" value="Submit F2" />
            }
        </div>
        <div>
            <p>Submit with a redirect, and no ViewModel usage.</p>
            @using (Html.BeginForm("Form3Submit", "Test"))
            {
                var inst3 = new TestController.ModelF3();
                <div>
                    @Html.HiddenFor(m => Model.InstT.Name)
                    @Html.LabelFor(m => inst3.Name)
                    @Html.TextBoxFor(m => inst3.Name)
                </div>
                <input type="submit" value="Submit F3" />
            }
        </div>
        <div>
            <p>Submit with a new class, and the ViewModel as well.</p>
            @using (Html.BeginForm("Form4Submit", "Test"))
            {
                var inst4 = new TestController.ModelF4();
                <div>
                    @Html.HiddenFor(m => Model.Message)
                    @Html.HiddenFor(m => Model.PostNumber)
                    @Html.HiddenFor(m => Model.Inst1.Name)
                    @Html.HiddenFor(m => Model.Inst2.Name)
                    @Html.HiddenFor(m => Model.InstT.Name)
                    @Html.LabelFor(m => inst4.Name)
                    @Html.TextBoxFor(m => inst4.Name)
                </div>
                <input type="submit" value="Submit F4" />
            }
        </div>

    </body>
</html>

【讨论】:

  • 这不是 View 了解控制器的设计缺陷吗?
猜你喜欢
  • 2017-11-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多