MVC 5通用列表绑定到部分视图

时间:2017-03-06 19:18:08

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

我遇到了将模型绑定到局部视图的问题,并且觉得必须有办法做我想做的事情。我想知道我的设计是否有缺陷,可能需要进行小的重构。

我的模型的非常简化(和抽象)版本就是这样:

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类型创建编辑器模板。所以我的观点看起来像这样:

  • DerivedClassView
    • BaseClassPartialView
      • 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.Editor加载其中一个列表时,它命名输入Collection[index].Property但是当我使用相同的调用动态添加一个时,它只是命名输入Property。是否有一种简单且可重复使用的方法来添加新项目具有相同的命名结构?

2 个答案:

答案 0 :(得分:0)

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

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

并且您认为,由于您的观点与DerivedClass合作并发布DerivedClass,因此您最终应该使用DerivedClass而不是BaseClass的实例在你的行动中。这个假设是不正确的。

post上存在的所有内容都是一组字符串键值对。模型绑定器查看操作签名并尝试将发布的数据绑定到参数的实例,并根据需要新增类。鉴于此,模型绑定器在此场景中的唯一信息是,它希望将值绑定到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])
}

答案 1 :(得分:0)

经过大量的研究,试验和错误以及Erik的帮助,我能够解决我的问题。问题结果是我在局部视图中命名表单元素。当模型添加时,它们将被编入索引:name = "ListProperty[Index].PropertyName"。当我使用ajax添加部分视图时,仅以属性名称命名。为了解决这个问题,我不得不像以下一样处理我对部分视图的ajax调用:

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