在 ASP.NET MVC 中使用带有 IEnumerable 模型的自定义编辑器模板的正确惯用方法 [英] Correct, idiomatic way to use custom editor templates with IEnumerable models in ASP.NET MVC

查看:31
本文介绍了在 ASP.NET MVC 中使用带有 IEnumerable 模型的自定义编辑器模板的正确惯用方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题是为什么我的 DisplayFor 没有循环遍历我的 IEnumerable?

<小时>

快速刷新.


A quick refresh.

什么时候:

  • 该模型具有 IEnumerable
  • 类型的属性
  • 您使用仅接受 lambda 表达式的重载将此属性传递给 Html.EditorFor()
  • 您在 Views/Shared/EditorTemplates 下有一个 T 类型的编辑器模板
  • the model has a property of type IEnumerable<T>
  • you pass this property to Html.EditorFor() using the overload that only accepts the lambda expression
  • you have an editor template for the type T under Views/Shared/EditorTemplates

然后 MVC 引擎将自动为可枚举序列中的每个项目调用编辑器模板,生成结果列表.

then the MVC engine will automatically invoke the editor template for each item in the enumerable sequence, producing a list of the results.

例如,当模型类 Order 具有属性 Lines 时:

E.g., when there is a model class Order with property Lines:

public class Order
{
    public IEnumerable<OrderLine> Lines { get; set; }
}

public class OrderLine
{
    public string Prop1 { get; set; }
    public int Prop2 { get; set; }
}

还有一个视图 Views/Shared/EditorTemplates/OrderLine.cshtml:

And there is a view Views/Shared/EditorTemplates/OrderLine.cshtml:

@model TestEditorFor.Models.OrderLine

@Html.EditorFor(m => m.Prop1)
@Html.EditorFor(m => m.Prop2)

然后,当您从顶级视图调用 @Html.EditorFor(m => m.Lines) 时,您将获得一个页面,其中包含每个订单行的文本框,而不仅仅是一个.

Then, when you invoke @Html.EditorFor(m => m.Lines) from the top-level view, you will get a page with text boxes for each order line, not just one.

但是,正如您在链接的问题中所见,这仅在您使用 EditorFor 的特定重载时才有效.如果您提供模板名称(为了使用不是以 OrderLine 类命名的模板),则不会发生自动序列处理,而是会发生运行时错误.

However, as you can see in the linked question, this only works when you use that particular overload of EditorFor. If you provide a template name (in order to use a template that is not named after the OrderLine class), then the automatic sequence handling will not happen, and a runtime error will happen instead.

此时您必须将自定义模板的模型声明为 IEnumebrable 并以某种方式手动迭代其项目以输出所有项目,例如

At which point you will have to declare your custom template's model as IEnumebrable<OrderLine> and manually iterate over its items in some way or another to output all of them, e.g.

@foreach (var line in Model.Lines) {
    @Html.EditorFor(m => line)
}

这就是问题开始的地方.

And that is where problems begin.

以这种方式生成的 HTML 控件都具有相同的 id 和名称.当你稍后POST它们时,模型绑定器将无法构造OrderLine的数组,并且你在控制器的HttpPost方法中获得的模型对象将是null.
如果您查看 lambda 表达式,这是有道理的 - 它并没有真正将正在构造的对象链接到它来自模型中的某个位置.

The HTML controls generated in this way all have same ids and names. When you later POST them, the model binder will not be able to construct an array of OrderLines, and the model object you get in the HttpPost method in the controller will be null.
This makes sense if you look at the lambda expression - it does not really link the object being constructed to a place in the model from which it comes.

我尝试了各种迭代项目的方法,似乎唯一的方法是将模板的模型重新声明为 IList 并使用 for 枚举它>:

I have tried various ways of iterating over the items, and it would seem the only way is to redeclare the template's model as IList<T> and enumerate it with for:

@model IList<OrderLine>

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

然后在顶级视图中:

@model TestEditorFor.Models.Order

@using (Html.BeginForm()) {
    @Html.EditorFor(m => m.Lines, "CustomTemplateName")
}

它提供了正确命名的 HTML 控件,这些控件在提交时被模型绑定器正确识别.

which gives properly named HTML controls that are properly recognized by the model binder on a submit.

虽然这行得通,但感觉很不对.

While this works, it feels very wrong.

使用带有 EditorFor 的自定义编辑器模板,同时保留允许引擎生成适用于模型绑定器的 HTML 的所有逻辑链接的正确惯用方法是什么?

What is the correct, idiomatic way to use a custom editor template with EditorFor, while preserving all the logical links that allow the engine to generate HTML suitable for the model binder?

推荐答案

与 Erik Funkenbusch 讨论之后,导致查看 MVC 源代码,看起来有两种更好的(正确的和惯用的?)方法来做到这一点.

After discussion with Erik Funkenbusch, which led to looking into the MVC source code, it would appear there are two nicer (correct and idiomatic?) ways to do it.

两者都涉及为帮助程序提供正确的 html 名称前缀,并生成与默认 EditorFor 的输出相同的 HTML.

Both involve providing correct html name prefix to the helper, and generate HTML identical to the output of the default EditorFor.

我暂时把它留在这里,我会做更多的测试以确保它在深度嵌套的场景中工作.

I'll just leave it here for now, will do more testing to make sure it works in deeply nested scenarios.

对于以下示例,假设您已经有两个用于 OrderLine 类的模板:OrderLine.cshtmlDifferentOrderLine.cshtml.

For the following examples, suppose you already have two templates for OrderLine class: OrderLine.cshtml and DifferentOrderLine.cshtml.

创建一个辅助模板,以任何名称保存(例如ManyDifferentOrderLines.cshtml"):

Create a helper template, saving it under any name (e.g. "ManyDifferentOrderLines.cshtml"):

@model IEnumerable<OrderLine>

@{
    int i = 0;

    foreach (var line in Model)
    { 
        @Html.EditorFor(m => line, "DifferentOrderLine", "[" + i++ + "]")
    }
}

然后从主订单模板调用它:

Then call it from the main Order template:

@model Order

@Html.EditorFor(m => m.Lines, "ManyDifferentOrderLines")

<小时>

方法 2 - IEnumerable

没有中间模板

在主订单模板中:


Method 2 - Without an intermediate template for IEnumerable<T>

In the main Order template:

@model Order

@{
    int i = 0;

    foreach (var line in Model.Lines)
    {
        @Html.EditorFor(m => line, "DifferentOrderLine", "Lines[" + i++ + "]")
    }
}

这篇关于在 ASP.NET MVC 中使用带有 IEnumerable 模型的自定义编辑器模板的正确惯用方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆