【问题标题】:getting the values from a nested complex object that is passed to a partial view从传递给局部视图的嵌套复杂对象中获取值
【发布时间】:2015-04-22 20:52:09
【问题描述】:

我有一个 ViewModel,它有一个复杂对象作为其成员之一。复杂对象有 4 个属性(所有字符串)。我正在尝试创建一个可重用的局部视图,我可以在其中传递复杂的对象并让它生成带有 html 助手的 html 的属性。这一切都很好。但是,当我提交表单时,模型绑定器不会将值映射回 ViewModel 的成员,因此我不会在服务器端得到任何东西。如何将用户键入的值读取到复杂对象的 html 助手中。

视图模型

public class MyViewModel
{
     public string SomeProperty { get; set; }
     public MyComplexModel ComplexModel { get; set; }
}

我的复杂模型

public class MyComplexModel
{
     public int id { get; set; }
     public string Name { get; set; }
     public string Address { get; set; }
     ....
}

控制器

public class MyController : Controller
{
     public ActionResult Index()
     {
          MyViewModel model = new MyViewModel();
          model.ComplexModel = new MyComplexModel();
          model.ComplexModel.id = 15;
          return View(model);
     }

     [HttpPost]
     public ActionResult Index(MyViewModel model)
     {
          // model here never has my nested model populated in the partial view
          return View(model);
     }
}

查看

@using(Html.BeginForm("Index", "MyController", FormMethod.Post))
{
     ....
     @Html.Partial("MyPartialView", Model.ComplexModel)
}

局部视图

@model my.path.to.namespace.MyComplexModel
@Html.TextBoxFor(m => m.Name)
...

如何在表单提交时绑定这些数据,以便父模型包含从局部视图在 Web 表单上输入的数据?

谢谢

编辑:我发现我需要在前面加上“ComplexModel”。到部分视图(文本框)中我的所有控件名称,以便它映射到嵌套对象,但我不能将 ViewModel 类型传递给部分视图以获得额外的层,因为它需要是通用的才能接受多个 ViewModel类型。我可以用 javascript 重写 name 属性,但这对我来说似乎过于贫民窟。我还能怎么做?

编辑 2:我可以使用 new { Name="ComplexModel.Name" } 静态设置 name 属性,所以我认为我在做生意,除非有人有更好的方法?

【问题讨论】:

    标签: asp.net-mvc model partial-views


    【解决方案1】:

    您可以将前缀传递给部分使用

    @Html.Partial("MyPartialView", Model.ComplexModel, 
        new ViewDataDictionary { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "ComplexModel" }})
    

    它将前缀附加到您控制name 属性,以便<input name="Name" ../> 将变为<input name="ComplexModel.Name" ../> 并在回发时正确绑定到typeof MyViewModel

    编辑

    为了更简单,你可以将它封装在一个 html 帮助器中

    public static MvcHtmlString PartialFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, string partialViewName)
    {
      string name = ExpressionHelper.GetExpressionText(expression);
      object model = ModelMetadata.FromLambdaExpression(expression, helper.ViewData).Model;
      var viewData = new ViewDataDictionary(helper.ViewData)
      {
        TemplateInfo = new System.Web.Mvc.TemplateInfo 
        { 
          HtmlFieldPrefix = string.IsNullOrEmpty(helper.ViewData.TemplateInfo.HtmlFieldPrefix) ? 
            name : $"{helper.ViewData.TemplateInfo.HtmlFieldPrefix}.{name}"
        }
      };
      return helper.Partial(partialViewName, model, viewData);
    }
    

    并将其用作

    @Html.PartialFor(m => m.ComplexModel, "MyPartialView")
    

    【讨论】:

    • 这看起来很有希望。我已经离开办公室了,但明天我回来时会试一试,如果它对我有用,请标记为答案。谢谢。
    • 嘘...我不能投票,因为它说我必须有 15 名声望。只要我得到 15 个代表,就为你 +1 :)
    • @BlairHolmes,添加了一个 html 辅助方法,使这更容易
    • @Christian,在某种程度上它已经是,因为正确的方法是使用 EditorFor() 和自定义 EditorTemplate 用于嵌套模型(并且 EditorFor() 方法将 HtmlFieldPrefix 传递为根据上面的代码)。部分并不是真正设计用于这种情况的,这就是为什么我认为 MVC 团队没有包括它)
    • EditorFor 的使用“过于传统”(严格的命名约定、文件夹结构),最终改变了输出 html。第二种解决方案是传递父模型,这也是一种错误的方法,因为它不再可重用。
    【解决方案2】:

    如果您使用标签助手,partial 标签助手接受 for 属性,这符合您的预期。

    <partial name="MyPartialView" for="ComplexModel" />
    

    使用for 属性,而不是典型的model 属性,将导致部分中的所有表单字段都以ComplexModel. 前缀命名。

    【讨论】:

      【解决方案3】:

      您可以尝试将 ViewModel 传递给部分。

      @model my.path.to.namespace.MyViewModel
      @Html.TextBoxFor(m => m.ComplexModel.Name)
      

      编辑

      您可以创建一个基础模型并将复杂模型推入其中并将基础模型传递给部分。

      public class MyViewModel :BaseModel
      {
          public string SomeProperty { get; set; }
      }
      
       public class MyViewModel2 :BaseModel
      {
          public string SomeProperty2 { get; set; }
      }
      
      public class BaseModel
      {
          public MyComplexModel ComplexModel { get; set; }
      }
      public class MyComplexModel
      {
          public int id { get; set; }
          public string Name { get; set; }
          ...
      }
      

      那么你的部分将如下所示:

      @model my.path.to.namespace.BaseModel
      @Html.TextBoxFor(m => m.ComplexModel.Name)
      

      如果这不是一个可接受的解决方案,您可能不得不考虑覆盖模型绑定器。你可以阅读here

      【讨论】:

      • 我不能这样做,因为它需要是通用的。将有几个父 ViewModel 需要传入它们自己的类型...请参阅我在 OP 中的第一次编辑。
      【解决方案4】:

      我遇到了同样的情况,在这些信息丰富的帖子的帮助下,我将部分代码更改为在部分视图生成的输入元素中生成前缀

      我使用了 Html.partial 帮助器,将 ModelType 的部分视图名称和对象以及带有 Html 字段前缀的 ViewDataDictionary 对象的实例提供给 Html.partial 的构造函数。

      这会导致“主视图”的“xyz url”的 GET 请求,并在其中渲染部分视图,其中输入元素带有前缀,例如较早的 Name="Title" 现在在相应的 HTML 元素中变为 Name="MySubType.Title" 并且对于其余的表单输入元素相同。

      当向“xyz url”发出 POST 请求时出现问题,期望填写的表单被保存到我的数据库中。但是 MVC Modelbinder 没有将我的 POSTed 模型数据与填写的表单值绑定,而且 ModelState 也丢失了。 viewdata 中的模型也即将为空。

      最后我尝试使用 TryUppdateModel 方法更新 Posted 表单中的模型数据,该方法接受模型实例和之前传递给局部视图的 html 前缀,现在可以看到模型与值绑定并且模型状态也存在。

      请让我知道这种方法是否可行或有点多样化!

      【讨论】:

        猜你喜欢
        • 2013-05-08
        • 2017-12-27
        • 1970-01-01
        • 2013-12-19
        • 1970-01-01
        • 2012-03-08
        • 2021-02-05
        相关资源
        最近更新 更多