将多个模型从一个视图传递到控制器

3
如果我在谷歌上搜索“一个视图中的多个模型”,我只能找到关于如何将模型传递给视图的结果。但是,我对“从视图到控制器”的方向很感兴趣。
因此,假设:
- 我有3个不同的表单和1个表格(WebGrid)在一个视图中。 - 并且每个表单都有一个模型,表格也有一个模型。 - 让我的模型类为ModelF1、ModelF2、ModelF3和ModelT。
到目前为止,我看到的所有示例都使用了一个容器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解析整个表单元素的情况下,这是否可能?


1
我已经编辑了你的标题。请参考“问题标题应该包含“标签”吗?”,在那里达成共识是“不应该”。 - John Saunders
1个回答

1

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

return View(model);

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

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

如果你只担心创建一个顶级ViewModel容器的效率感知问题,那就别担心了。这比你可能需要采取的所有解决方案更有效,以便在没有顶级ViewModel的情况下让良好的表单工作。

下面有一些示例代码。下面的代码应该演示了使用包含在顶级ViewModel中的模型只是更简单和更整洁的:有些表单故意不往返某些状态。请注意使用HiddenForModelState.Clear,它们都与你试图做的事情有关,但即使这些也无法为Form4Submit中的inst4.Name保留值。探索的各种选项如下:

  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>

1
这不是一个设计缺陷吗?View 知道控制器的存在? - A Coder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接