如何防止Razor在使用嵌套显示模板时向输入中添加前缀?

时间:2023-02-14 21:02:35

When I use nested display templates and add input elements through the HTML helper the Razor engine adds a prefix to the fields names.

当我使用嵌套显示模板并通过HTML助手添加输入元素时,Razor引擎会向字段名添加一个前缀。

I do understand this is done to guarantee input name uniqueness at page level (and to rebuild the whole model on post back).

我知道这样做是为了保证在页面级别上输入名称的唯一性(以及在post - back上重建整个模型)。

However I have many small forms which perform ad-hoc actions, and I don't need neither the name uniqueness nor the ability to rebuild the whole model.

但是,我有许多执行临时操作的小表单,我既不需要名称惟一性,也不需要重新构建整个模型的能力。

I just need that single property value, and having Razor alter the input items names breaks the model binder when I submit one of the forms, since all the names will be different.

我只需要那个属性值,当我提交一个表单时,让Razor修改输入项名称会破坏模型绑定,因为所有的名称都是不同的。

This example contains a simplified nested model

这个例子包含一个简化的嵌套模型

public class Student
{
    public Guid Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public List<Course> Courses { get; set; }
}

public class Course
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public List<Grade> Grades { get; set; }
}

public class Grade
{
    public Guid Id { get; set; }
    public DateTime Date { get; set; }
    public decimal Value { get; set; }
}

and it has an Index view with three nested display templates

它有一个索引视图和三个嵌套显示模板

IndexView
    StudentDisplayTemplate
        CourseDisplayTemplate
            GradeDisplayTemplate

In the grade display template I add a button to remove the grade

在等级显示模板中,我添加了一个按钮来删除等级

@model Playground.Sandbox.Models.Home.Index.Grade

<li>
    @this.Model.Date: @this.Model.Value

    @using (Html.BeginForm("Remove", "Home", FormMethod.Post))
    {
        <input name="GradeId" type="hidden" value="@this.Model.Id" />

        <input type="submit" value="Remove" />
    }
</li>

and on the other side of the request my controller action receives the grade ID

在请求的另一边,我的控制器动作接收等级ID

public ActionResult Remove(Guid id)
{
    // Do various things.
    return this.RedirectToAction("Index");
}

If I try to do it using the model helper

如果我试着用模型助手来做

@Html.HiddenFor(x => x.Id)

I get the HTML element

得到HTML元素

<input data-val="true"
       data-val-required="The Id field is required."
       id="Courses_0__Grades_1__Id"
       name="Courses[0].Grades[1].Id"
       type="hidden"
       value="76f7e7ed-a479-42cb-add5-e58c0090770c" />

where the field name gets a prefix based on the whole parent's view model tree.

字段名称在整个父视图模型树的基础上得到前缀。

Using the "manual" helper

使用“手册”的帮手

@Html.Hidden("GradeId", this.Model.Id)

gives the HTML element

给HTML元素

<input id="Courses_0__Grades_0__GradeId"
       name="Courses[0].Grades[0].GradeId"
       type="hidden"
       value="bbb3c11d-d2d0-464a-b33b-ff7ac9815601" />

where the prefix is still present, albeit with my name at the end.

这里的前缀仍然存在,尽管最后还有我的名字。

Adding manually the hidden input

手动添加隐藏的输入

<input name="GradeId" type="hidden" value="@this.Model.Id" />

gives the HTML element

给HTML元素

<input name="GradeId"
       type="hidden"
       value="a1a35e81-29cd-41b5-b619-bab79b767613" />

which is what I want.

这就是我想要的。

Is it possible to achieve what I want, or am I getting the display templates thing wrong?

是否有可能实现我想要的,或者我得到显示模板的东西是错误的?

2 个解决方案

#1


10  

You want to set ViewData.TemplateInfo.HtmlFieldPrefix in your Grade template:

您需要设置ViewData.TemplateInfo。年级模板中的HtmlFieldPrefix:

@model Playground.Sandbox.Models.Home.Index.Grade

@{
    ViewData.TemplateInfo.HtmlFieldPrefix = "";
}

This gives the desired output of:

这就得到了期望的输出:

<input id="GradeId" name="GradeId" type="hidden" />

#2


0  

From your question, it's not clear if you're looking to be able to submit both the entire form, or just the individual actions. If you want to be able to do both, I think you'll have to use a JS-based solution to manually submit the subform requests.

从你的问题来看,不清楚你是想同时提交整个表单,还是只提交单个动作。如果您想同时完成这两项工作,我认为您必须使用基于jms的解决方案来手动提交子表单请求。

However, if you just want to submit the subforms, piecemeal, read along..

但是,如果你只是想提交子表单,请逐段阅读。

If the syntax Courses_0__Grades_1__Id for collections is causing problems, this is relatively easy to fix, in my experience.

如果集合的语法Courses_0__Grades_1__Id导致问题,根据我的经验,这相对容易修复。

You can get different behavior on how the names/ids are generated in child objects in collections by using foreach instead of a traditional for.

通过使用foreach而不是传统的for,您可以获得关于在集合中的子对象中如何生成名称/id的不同行为。

Let me explain:

让我来解释一下:


1) This will break the model binding for entire-form submission. All inputs for child items will have no context of their parent path.

1)这将破坏实体表单提交的模型绑定。子项的所有输入都没有其父路径的上下文。

@foreach(var child in Model.Children)
{
    @Html.EditorFor(x=> child)
}


2) These will respect parent context and allow model binding for entire-form submission. All inputs for child items WILL HAVE context of their parent path.

2)它们将尊重父上下文并允许模型绑定用于实体表单提交。子项的所有输入都有其父路径的上下文。

@for(var i = 0; i < Model.Children.Count(); i++)
{
    @Html.EditorFor(x=> Model.Children[i])
}
// or..
var i = 0; 
@foreach(var child in Model.Children)
{
    @Html.EditorFor(x=> Model.Children[i])
    i++;
}

However

然而

You will still have issues with objects NOT in collections, hanging off the main model, like Model.SomeOtherType.AnotherType will have inputs in the nested EditorFor with names like SomeOtherType.Property1 and SomeOtherType.Property2

您仍然会遇到一些问题,对象不在集合中,挂在主模型上,比如模型。另一种类型在嵌套的编辑器中有输入,其名称类似于其他类型。Property1和SomeOtherType.Property2

For these, you can pull the object into a temporary variable in the razor:

对于这些,您可以将对象拉到razor中的一个临时变量中:

@var tempObj = Model.SomeOtherType;
<div class='somemarkup'>
    @Html.EditorFor(x=> tempObj);
</div>

#1


10  

You want to set ViewData.TemplateInfo.HtmlFieldPrefix in your Grade template:

您需要设置ViewData.TemplateInfo。年级模板中的HtmlFieldPrefix:

@model Playground.Sandbox.Models.Home.Index.Grade

@{
    ViewData.TemplateInfo.HtmlFieldPrefix = "";
}

This gives the desired output of:

这就得到了期望的输出:

<input id="GradeId" name="GradeId" type="hidden" />

#2


0  

From your question, it's not clear if you're looking to be able to submit both the entire form, or just the individual actions. If you want to be able to do both, I think you'll have to use a JS-based solution to manually submit the subform requests.

从你的问题来看,不清楚你是想同时提交整个表单,还是只提交单个动作。如果您想同时完成这两项工作,我认为您必须使用基于jms的解决方案来手动提交子表单请求。

However, if you just want to submit the subforms, piecemeal, read along..

但是,如果你只是想提交子表单,请逐段阅读。

If the syntax Courses_0__Grades_1__Id for collections is causing problems, this is relatively easy to fix, in my experience.

如果集合的语法Courses_0__Grades_1__Id导致问题,根据我的经验,这相对容易修复。

You can get different behavior on how the names/ids are generated in child objects in collections by using foreach instead of a traditional for.

通过使用foreach而不是传统的for,您可以获得关于在集合中的子对象中如何生成名称/id的不同行为。

Let me explain:

让我来解释一下:


1) This will break the model binding for entire-form submission. All inputs for child items will have no context of their parent path.

1)这将破坏实体表单提交的模型绑定。子项的所有输入都没有其父路径的上下文。

@foreach(var child in Model.Children)
{
    @Html.EditorFor(x=> child)
}


2) These will respect parent context and allow model binding for entire-form submission. All inputs for child items WILL HAVE context of their parent path.

2)它们将尊重父上下文并允许模型绑定用于实体表单提交。子项的所有输入都有其父路径的上下文。

@for(var i = 0; i < Model.Children.Count(); i++)
{
    @Html.EditorFor(x=> Model.Children[i])
}
// or..
var i = 0; 
@foreach(var child in Model.Children)
{
    @Html.EditorFor(x=> Model.Children[i])
    i++;
}

However

然而

You will still have issues with objects NOT in collections, hanging off the main model, like Model.SomeOtherType.AnotherType will have inputs in the nested EditorFor with names like SomeOtherType.Property1 and SomeOtherType.Property2

您仍然会遇到一些问题,对象不在集合中,挂在主模型上,比如模型。另一种类型在嵌套的编辑器中有输入,其名称类似于其他类型。Property1和SomeOtherType.Property2

For these, you can pull the object into a temporary variable in the razor:

对于这些,您可以将对象拉到razor中的一个临时变量中:

@var tempObj = Model.SomeOtherType;
<div class='somemarkup'>
    @Html.EditorFor(x=> tempObj);
</div>