ASP.NET MVC局部视图:输入名称前缀

que*_*en3 119 asp.net-mvc spark-view-engine mvccontrib

假设我有ViewModel之类的

public class AnotherViewModel
{
   public string Name { get; set; }
}
public class MyViewModel
{
   public string Name { get; set; }
   public AnotherViewModel Child { get; set; }
   public AnotherViewModel Child2 { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

在视图中,我可以渲染部分

<% Html.RenderPartial("AnotherViewModelControl", Model.Child) %>
Run Code Online (Sandbox Code Playgroud)

在部分我会做

<%= Html.TextBox("Name", Model.Name) %>
or
<%= Html.TextBoxFor(x => x.Name) %>
Run Code Online (Sandbox Code Playgroud)

但是,问题是两者都将呈现name ="Name",而我需要name ="Child.Name"才能使模型绑定器正常工作.或者,当我使用相同的局部视图渲染第二个属性时,name ="Child2.Name".

如何使我的局部视图自动识别所需的前缀?我可以将它作为参数传递,但这太不方便了.当我想要例如以递归方式呈现它时,情况会更糟.有没有办法用前缀渲染部分视图,或者更好的是,通过自动重新调用lambda表达式来实现

<% Html.RenderPartial("AnotherViewModelControl", Model.Child) %>
Run Code Online (Sandbox Code Playgroud)

会自动添加正确的"孩子".生成的名称/ id字符串的前缀?

我可以接受任何解决方案,包括第三方视图引擎和库 - 我实际上使用Spark View引擎(我使用它的"解决"问题)和MvcContrib,但没有在那里找到解决方案.XForms,InputBuilder,MVC v2 - 提供此功能的任何工具/洞察都会很棒.

目前我考虑自己编码,但这似乎是浪费时间,我不敢相信这些琐碎的东西还没有实现.

可能存在许多手动解决方案,欢迎所有这些解决方案.例如,我可以强制我的部分基于IPartialViewModel <T> {public string Prefix; T模型; }.但我宁愿选择一些现有/已批准的解决方案.

更新:有没有回答类似的问题在这里.

Mah*_*vej 109

您可以通过以下方式扩展Html帮助程序类:

using System.Web.Mvc.Html


 public static MvcHtmlString PartialFor<TModel, TProperty>(this HtmlHelper<TModel> helper, System.Linq.Expressions.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 = name
            }
        };

        return helper.Partial(partialViewName, model, viewData);

    }
Run Code Online (Sandbox Code Playgroud)

并在这样的视图中使用它:

<%= Html.PartialFor(model => model.Child, "_AnotherViewModelControl") %>
Run Code Online (Sandbox Code Playgroud)

你会看到一切都好!

  • 对于嵌套的部分渲染,这将是不正确的.您需要以``oldprefix}.{newprefix}的形式从`helper.ViewData.TemplateInfo.HtmlFieldPrefix`追加旧前缀的新前缀. (16认同)
  • @IvanZlatev是对的.在设置名称之前,你应该做```string oldPrefix = helper.ViewData.TemplateInfo.HtmlFieldPrefix; if(oldPrefix!="")name = oldPrefix +"." +名字;``` (2认同)
  • 解决嵌套模板的一个简单方法是使用HtmlFieldPrefix = helper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name) (2认同)

Jok*_*kin 93

到目前为止,我正在寻找我最近发现这篇文章的相同内容:

http://davybrion.com/blog/2011/01/prefixing-input-elements-of-partial-views-with-asp-net-mvc/

<% Html.RenderPartial("AnotherViewModelControl", Model.Child, new ViewDataDictionary
{
    TemplateInfo = new System.Web.Mvc.TemplateInfo { HtmlFieldPrefix = "Child1" }
})
%>
Run Code Online (Sandbox Code Playgroud)

  • 这个派对已经很晚了,但如果你采用这种方法,你应该使用带有当前`ViewData`的`ViewDataDictionary`构造函数,否则你会丢失模型状态错误,验证数据等. (31认同)
  • 建立在巴林的评论上.你也可以传递嵌套前缀,例如(sry这是vb.net ex):Html.RenderPartial("AnotherViewModelControl",Model.PropX,New ViewDataDictionary(ViewData){{.TemplateInfo = New TemplateInfo()With {.HtmlFieldPrefix = ViewData.TemplateInfo.HtmlFieldPrefix&"..PropX"}}) (9认同)
  • 感谢您的链接,这是目前为止列出的最佳选择 (3认同)
  • 很好的答案,+1。也许值得对其进行编辑以考虑@bhamlin 评论 (2认同)
  • 请注意,如果您需要从控制器返回此部分,您还需要在那里设置此前缀。http://stackoverflow.com/questions/6617768/asp-net-mvc3-add-a-htmlfieldprefix-when-calling-controller-partialview (2认同)

aso*_*879 12

我的回答是基于Mahmoud Moravej的回答,包括Ivan Zlatev的评论.

    public static MvcHtmlString PartialFor<TModel, TProperty>(this HtmlHelper<TModel> helper, System.Linq.Expressions.Expression<Func<TModel, TProperty>> expression, string partialViewName)
    {
            string name = ExpressionHelper.GetExpressionText(expression);
            object model = ModelMetadata.FromLambdaExpression(expression, helper.ViewData).Model;
            StringBuilder htmlFieldPrefix = new StringBuilder();
            if (helper.ViewData.TemplateInfo.HtmlFieldPrefix != "")
            {
                htmlFieldPrefix.Append(helper.ViewData.TemplateInfo.HtmlFieldPrefix);
                htmlFieldPrefix.Append(name == "" ? "" : "." + name);
            }
            else
                htmlFieldPrefix.Append(name);

            var viewData = new ViewDataDictionary(helper.ViewData)
            {
                TemplateInfo = new System.Web.Mvc.TemplateInfo
                {
                    HtmlFieldPrefix = htmlFieldPrefix.ToString()
                }
            };

        return helper.Partial(partialViewName, model, viewData);
    }
Run Code Online (Sandbox Code Playgroud)

编辑:对于嵌套的部分渲染,穆罕默德的答案是不正确的.只有在必要时才需要将新前缀附加到旧前缀.这在最新的答案中并不清楚(:

  • 如果您详细说明上述内容如何改进现有答案,对其他人会有所帮助. (6认同)
  • 这个版本非常适合我。 (2认同)
  • 在出现繁琐的复制和粘贴错误之后,该脚本非常适合ASP.NET MVC5。+1。 (2认同)

Nic*_*sen 9

使用MVC2可以实现这一目标.

这是强类型视图:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MvcLearner.Models.Person>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Create
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Create</h2>

    <% using (Html.BeginForm()) { %>
        <%= Html.LabelFor(person => person.Name) %><br />
        <%= Html.EditorFor(person => person.Name) %><br />
        <%= Html.LabelFor(person => person.Age) %><br />
        <%= Html.EditorFor(person => person.Age) %><br />
        <% foreach (String FavoriteFoods in Model.FavoriteFoods) { %>
            <%= Html.LabelFor(food => FavoriteFoods) %><br />
            <%= Html.EditorFor(food => FavoriteFoods)%><br />
        <% } %>
        <%= Html.EditorFor(person => person.Birthday, "TwoPart") %>
        <input type="submit" value="Submit" />
    <% } %>

</asp:Content>
Run Code Online (Sandbox Code Playgroud)

这是子类的强类型视图(必须存储在名为EditorTemplates的视图目录的子文件夹中):

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MvcLearner.Models.TwoPart>" %>

<%= Html.LabelFor(birthday => birthday.Day) %><br />
<%= Html.EditorFor(birthday => birthday.Day) %><br />

<%= Html.LabelFor(birthday => birthday.Month) %><br />
<%= Html.EditorFor(birthday => birthday.Month) %><br />
Run Code Online (Sandbox Code Playgroud)

这是控制器:

public class PersonController : Controller
{
    //
    // GET: /Person/
    [AcceptVerbs(HttpVerbs.Get)]
    public ActionResult Index()
    {
        return View();
    }

    [AcceptVerbs(HttpVerbs.Get)]
    public ActionResult Create()
    {
        Person person = new Person();
        person.FavoriteFoods.Add("Sushi");
        return View(person);
    }

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Create(Person person)
    {
        return View(person);
    }
}
Run Code Online (Sandbox Code Playgroud)

以下是自定义类:

public class Person
{
    public String Name { get; set; }
    public Int32 Age { get; set; }
    public List<String> FavoriteFoods { get; set; }
    public TwoPart Birthday { get; set; }

    public Person()
    {
        this.FavoriteFoods = new List<String>();
        this.Birthday = new TwoPart();
    }
}

public class TwoPart
{
    public Int32 Day { get; set; }
    public Int32 Month { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

和输出源:

<form action="/Person/Create" method="post"><label for="Name">Name</label><br /> 
    <input class="text-box single-line" id="Name" name="Name" type="text" value="" /><br /> 
    <label for="Age">Age</label><br /> 
    <input class="text-box single-line" id="Age" name="Age" type="text" value="0" /><br /> 
    <label for="FavoriteFoods">FavoriteFoods</label><br /> 
    <input class="text-box single-line" id="FavoriteFoods" name="FavoriteFoods" type="text" value="Sushi" /><br /> 
    <label for="Birthday_Day">Day</label><br /> 
    <input class="text-box single-line" id="Birthday_Day" name="Birthday.Day" type="text" value="0" /><br /> 

    <label for="Birthday_Month">Month</label><br /> 
    <input class="text-box single-line" id="Birthday_Month" name="Birthday.Month" type="text" value="0" /><br /> 
    <input type="submit" value="Submit" /> 
</form>
Run Code Online (Sandbox Code Playgroud)

现在这已经完成了.在Create Post控制器操作中设置断点以进行验证.不要将它与列表一起使用,因为它不起作用.有关更多信息,请参阅我使用带有IEnumerable的EditorTemplates的问题.


R. *_*urs 9

这是一个老问题,但对于到此处寻找解决方案的任何人来说,请考虑使用EditorFor,如/sf/answers/2086693521/中的评论所示.要从部分视图移动到编辑器模板,请按照下列步骤操作.

  1. 验证您的局部视图是否绑定到ComplexType.

  2. 将局部视图移动到当前视图文件夹的子文件夹EditorTemplatesShared文件夹.现在,它是一个编辑器模板.

  3. 更改@Html.Partial("_PartialViewName", Model.ComplexType)@Html.EditorFor(m => m.ComplexType, "_EditorTemplateName").如果编辑器模板是复杂类型的唯一模板,则该模板是可选的.

Html输入元素将自动命名ComplexType.Fieldname.


Rah*_*oon 8

PartailFor for asp.net Core 2以防有人需要它.

    public static ModelExplorer GetModelExplorer<TModel, TResult>(this IHtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TResult>> expression)
    {
        if (expression == null)
            throw new ArgumentNullException(nameof(expression));
        return ExpressionMetadataProvider.FromLambdaExpression(expression, htmlHelper.ViewData, htmlHelper.MetadataProvider);
    }

    public static IHtmlContent PartialFor<TModel, TResult>(this IHtmlHelper<TModel> helper, Expression<Func<TModel, TResult>> expression, string partialViewName, string prefix = "")
    {
        var modelExplorer = helper.GetModelExplorer(expression);
        var viewData = new ViewDataDictionary(helper.ViewData);
        viewData.TemplateInfo.HtmlFieldPrefix += prefix;
        return helper.Partial(partialViewName, modelExplorer.Model, viewData);
    }
Run Code Online (Sandbox Code Playgroud)


rom*_*oza 6

如此处所述: https: //stackoverflow.com/a/58943378/3901618 - 对于 ASP.NET Core - 您可以使用部分标记帮助器。

<partial name="AnotherViewModelControl" for="Child" />
<partial name="AnotherViewModelControl" for="Child2" />
Run Code Online (Sandbox Code Playgroud)

它生成所有必需的名称前缀。