从传递给分部视图的嵌套复杂对象中获取值

问题描述 投票:0回答:5

我有一个 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
5个回答
44
投票

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

@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")

10
投票

如果您使用标签助手,则

partial
标签助手接受
for
属性,该属性会执行您所期望的操作。

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

使用

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


2
投票

您可以尝试将 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)

如果这不是一个可接受的解决方案,您可能必须考虑覆盖模型绑定器。您可以在此处阅读相关内容。


0
投票

我遇到了同样的情况,并在此类信息帖子的帮助下更改了我的部分代码,以在部分视图生成的输入元素中生成前缀

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

这会导致“主视图”的“xyz url”的 GET 请求,并在其中渲染部分视图,并使用前缀生成的输入元素,例如先前的 Name="Title" 现在在相应的 HTML 元素中变为 Name="MySubType.Title",其余表单输入元素也相同。

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

最后,我尝试使用 TryUppdateModel 方法以发布形式更新模型数据,该方法采用模型实例和 html 前缀(之前传递到部分视图),并且现在可以看到模型与值绑定,并且模型状态也存在。

请告诉我这种方法是否良好或有点多样化!


0
投票

我遇到了这个问题和解决方案,但我使用的是 dotnet core。所以我使用了 David Carek 的解决方案并将其转换为 dotnet 核心变体:

public static class HtmlHelperExtensions
{
    public static Task<IHtmlContent> PartialForAsync<TModel, TProperty>(
        this IHtmlHelper<TModel> htmlHelper,
        Expression<Func<TModel, TProperty>> expression,
        string partialViewName
    )
    {
        if (htmlHelper == null)
        {
            throw new ArgumentNullException(nameof(htmlHelper));
        }

        if (expression == null)
        {
            throw new ArgumentNullException(nameof(expression));
        }

        if (partialViewName == null)
        {
            throw new ArgumentNullException(nameof(partialViewName));
        }

        string name = htmlHelper.GetExpressionText(expression);
        object model = htmlHelper.GetModelExpression(expression).Model;
        var viewData = new ViewDataDictionary(htmlHelper.ViewData)
        {
            TemplateInfo =
            {
                HtmlFieldPrefix = string.IsNullOrEmpty(htmlHelper.ViewData.TemplateInfo.HtmlFieldPrefix)
                    ? name
                    : $"{htmlHelper.ViewData.TemplateInfo.HtmlFieldPrefix}{(name.StartsWith('[') ? "" : ".")}{name}"
            }
        };

        return htmlHelper.PartialAsync(partialViewName, model, viewData);
    }

    public static string GetExpressionText<TModel, TResult>(
        this IHtmlHelper<TModel> htmlHelper,
        Expression<Func<TModel, TResult>> expression)
    {
        var expressionProvider = htmlHelper.ViewContext.HttpContext.RequestServices
            .GetService(typeof(ModelExpressionProvider)) as ModelExpressionProvider;

        if (expressionProvider == null)
        {
            throw new InvalidOperationException("Unable to retrieve ModelExpressionProvider from DI");
        }

        return expressionProvider.GetExpressionText(expression);
    }

    public static ModelExpression GetModelExpression<TModel, TResult>(
        this IHtmlHelper<TModel> htmlHelper,
        Expression<Func<TModel, TResult>> expression)
    {
        var expressionProvider = htmlHelper.ViewContext.HttpContext.RequestServices
            .GetService(typeof(ModelExpressionProvider)) as ModelExpressionProvider;

        if (expressionProvider == null)
        {
            throw new InvalidOperationException("Unable to retrieve ModelExpressionProvider from DI");
        }

        return expressionProvider.CreateModelExpression(htmlHelper.ViewData, expression);
    }
}

用法也有所调整:

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

我还添加了一个小调整,其中没有添加

.
,以防您的模型表达式以
[
开头,以便能够处理列表(例如
list[1]
)。

如果有人知道在 dotnet Core 中达到相同结果的更好方法,请随时做出反应。

© www.soinside.com 2019 - 2024. All rights reserved.