【问题标题】:MVC 5 Generic List Binding to Partial ViewMVC 5 通用列表绑定到部分视图
【发布时间】:2017-03-11 02:21:15
【问题描述】:

我的模型绑定到局部视图时遇到问题,我觉得必须有一种方法来做我想做的事。我想知道我的设计是否有缺陷,可能需要进行小的重构。

我的模型的一个非常简化(和抽象)的版本应该是这样的:

Public Class BaseClass
{
    Public string Name { get; set; }
    Public List<SomeClass> Things { get; set; }
}

Public Class DerivedClass : BaseClass
{
    Public List<LineItem> Items { get; set; }
}

Public Class Library
{
    Public List<LineItem> Items { get; set; }
}

Public Class LineItem
{
    Public string Name { get; set; }
    Public string Value { get; set; }
}

我有 BaseClass、SomeClass 和 LineItem 的编辑器模板。这些显示在 DerivedClass 的视图中,并通过向控制器提交更改来按预期工作。 LineItem 模板包含在 LineItemList 部分视图中,因为我打算将其用于 Library 的视图,并且不想重复所有该布局和 javascript。 LineItemList 部分视图包含在 Html.PartialView 的 DerivedClass 视图中,因为似乎没有办法为 List 类型创建编辑器模板。所以我的观点是这样的:

  • 派生类视图
    • 基类部分视图
      • SomeClassPartialView
    • LineItemListPartialView
      • LineItemParialView

当我提交表单时,控制器获取 BaseClass 和 SomeClass 列表的所有数据,但没有获取 LineItem 列表的数据。不同之处当然是一个使用 Html.EditorFor 和另一个 Html.PartialView 呈现。

重构类会很困难,因为它们必须向后兼容旧的 XML 格式以进行序列化,但我相信如果有必要我可以发挥一些神奇的作用。

正如 Chris Pratt 提到的,我忘记包含我的控制器方法:

Public ActionResult DerivedClassEditor()
{
    Return View(New DerivedClass());
}

[HttpPost]
Public ActionResult DerivedClassEditor(DerivedClass dc)
{
    // Do Stuff
}

我刚刚注意到在呈现的 Html 中,SomeClass 控件的名称为 SomeClass.[0].Name,而 LineItem 的名称为 [0].Name。我有一种感觉,这可能是问题的征兆。

而我的观点与此类似:

DerivedClassEditor

@model DerivedClass

@using (Html.BeginForm())
{
    @Html.EditorFor(model => model)

    @Html.Partial("LineItemListPartialView")

    <input type="submit" />
}

LineItemListPartialView

@model List<LineItem>

<div name="Items">
    @Html.EditorFor(model => model)
</div>

LineItemPartialView

@model LineItem

<div name="LineItem">
    @Html.LabelFor(model => model.Name)
    @Html.TextEditorFor(model => model.Name)
</div>

编辑

我的观点的链接:https://github.com/melance/TheRandomizer/blob/Initial/TheRandomizer.WebApp/Views/UserContent/AssignmentEditor.cshtml

我已经将问题缩小到这样一个事实,即当我使用@Html.EditorFor 加载其中一个列表时,它会将输入命名为Collection[index].Property,但是当我使用相同的调用动态添加一个列表时,它只是将输入命名为@987654331 @。是否有一种简单且可重用的方法让添加的新项目具有相同的命名结构?

【问题讨论】:

  • 您对命名的最后评论表明您没有使用 EditorFor 渲染 LineItems,而是以某种方式使用部分。你真的应该包含你的视图代码,这样我们就可以看到发生了什么。
  • @ErikFunkenbusch,我将尝试添加我正在做的事情的简化版本。我不想让这个问题太长。
  • 这里是github中的实际视图:github.com/melance/TheRandomizer/blob/Initial/…
  • 您上面的代码与您实际视图中的代码不同,您遗漏了一个关键部分,即您使用子模型调用部分。这就是你的问题所在。执行此操作时,您会丢失生成名称中模型命名的层次结构。我建议您传递基本模型(或者更确切地说,将其留空,就像您在示例中所做的那样),然后让您的部分采用 DerivedClass 模型而不是 List&lt;LineItem&gt; 模型。这里的问题是,partial 不理解它的模型是更大视图模型的一部分,因此它不会为它生成名称。
  • 谢谢你,Erik,我该如何做这件事,并且仍然保持局部视图足够通用以处理 Library 对象?

标签: c# asp.net-mvc-5


【解决方案1】:

至关重要的是,你没有发布你的控制器代码,所以我在黑暗中刺伤,但我想我可以很好地猜测发生了什么。你很可能有类似的东西:

[HttpPost]
public ActionResult MyAwesomeAction(BaseClass model)
{
    ...
}

并且您假设由于您的视图正在使用DerivedClass 并发布DerivedClass,因此您应该在操作中最终得到DerivedClass 而不是BaseClass 的实例。这个假设是不正确的。

post 中存在的只是一组字符串键值对。 modelbinder 查看动作签名并尝试将发布的数据绑定到参数的实例,并根据需要更新类。鉴于此,modelbinder 在这种情况下拥有的唯一信息是它应该将值绑定到 BaseClass 的实例和一组数据以尝试这样做。结果,它将创建一个BaseClass 的实例并绑定它可以绑定的数据,丢弃它不能绑定的任何数据。重要的是,由于BaseClass 不包含您的Items 属性之类的内容,所有这些数据都将被丢弃。

动作参数不支持长短多态。您的参数类型必须是您想要的类型。期间。

对于它的价值,您可以使用编辑器模板来处理列表属性。 EditorFor 将简单地为列表中的每个项目呈现包含类型的编辑器模板。例如,调用:

@Html.EditorFor(m => m.Items)

本质上等同于:

@for (var i = 0; i < Model.Items.Count(); i++)
{
    @Html.EditorFor(m => m.Items[i])
}

【讨论】:

  • 不幸的是,这就是我正在做的事情。我的编辑器的模型是 DerivedClass,Controller 方法的参数也是。我将此添加到我最初的问题中以进行澄清。我无意中发现 EditorFor 使用了 List,谢谢分享。
【解决方案2】:

因此,经过大量研究、反复试验和 Erik 的帮助,我能够解决我的问题。在我的部分观点中,问题原来是表单元素的命名。当模型添加它们时,它们的索引如下:name = "ListProperty[Index].PropertyName"。当我使用 ajax 添加部分视图时,它们仅以属性名称命名。为了解决这个问题,我必须像这样处理局部视图的 ajax 调用:

public ActionResult CreateParameter(Int32 index)
{
    ViewData.TemplateInfo.HtmlFieldPrefix = $"Parameters[{index}]";
    return PartialView("~/Views/Shared/EditorTemplates/Configuration.cshtml");
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-09
    • 2011-07-08
    • 2023-03-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多