假设我有像
这样的ViewModelpublic 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; }
}
在视图中,我可以使用
渲染部分内容<% Html.RenderPartial("AnotherViewModelControl", Model.Child) %>
在部分我会做
<%= Html.TextBox("Name", Model.Name) %>
or
<%= Html.TextBoxFor(x => x.Name) %>
然而,问题是两者都将呈现name =“Name”,而我需要name =“Child.Name”才能使模型绑定器正常工作。或者,当我使用相同的局部视图渲染第二个属性时,name =“Child2.Name”。
如何让我的局部视图自动识别所需的前缀?我可以将它作为参数传递,但这太不方便了。当我想要例如以递归方式呈现它时,情况会更糟。有没有办法使用前缀渲染部分视图,或者更好的是,通过自动重新调用lambda表达式来实现
<% Html.RenderPartial("AnotherViewModelControl", Model.Child) %>
会自动添加正确的“Child”。生成的名称/ id字符串的前缀?
我可以接受任何解决方案,包括第三方视图引擎和库 - 我实际上使用Spark View引擎(我使用其宏“解决”问题)和MvcContrib,但没有在那里找到解决方案。 XForms,InputBuilder,MVC v2 - 提供此功能的任何工具/洞察都会很棒。
目前我考虑自己编码,但这似乎是浪费时间,我不敢相信这些琐碎的东西还没有实现。
可能存在许多手动解决方案,欢迎所有这些解决方案。例如,我可以强制我的部分基于IPartialViewModel&lt; T&gt; {public string Prefix; T模型; }。但我宁愿选择一些现有/已批准的解决方案。
更新:有一个类似的问题,没有回答here。
答案 0 :(得分:107)
您可以通过以下方式扩展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);
}
只需在您的观看中使用它:
<%= Html.PartialFor(model => model.Child, "_AnotherViewModelControl") %>
你会看到一切都好!
答案 1 :(得分:90)
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" }
})
%>
答案 2 :(得分:11)
我的答案,基于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);
}
编辑: 对于嵌套的部分渲染,Mohamoud的答案是不正确的。只有在必要时,才需要将新前缀附加到旧前缀。这在最新的答案中并不清楚(:
答案 3 :(得分: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>
这是子类的强类型视图(必须存储在名为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 />
这是控制器:
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);
}
}
以下是自定义类:
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; }
}
输出源:
<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>
现在已经完成了。在Create Post控制器操作中设置断点以进行验证。不要将它与列表一起使用,因为它不起作用。请参阅我关于使用带有IEnumerable的EditorTemplates的问题。
答案 4 :(得分:8)
这是一个老问题,但对于到此处寻找解决方案的任何人来说,请考虑使用EditorFor
,如https://www.relishapp.com/rspec/rspec-core/v/2-0/docs/example-groups/shared-example-group中的评论所示。要从部分视图移动到编辑器模板,请按照下列步骤操作。
确认您的部分视图已绑定到 ComplexType 。
将局部视图移动到当前视图文件夹的子文件夹 EditorTemplates ,或移动到文件夹 Shared 。现在,它是一个编辑器模板。
将@Html.Partial("_PartialViewName", Model.ComplexType)
更改为@Html.EditorFor(m => m.ComplexType, "_EditorTemplateName")
。如果编辑器模板是复杂类型的唯一模板,则该编辑器模板是可选的。
Html输入元素将自动命名为ComplexType.Fieldname
。
答案 5 :(得分:7)
PartailFor 。
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);
}
答案 6 :(得分:3)
我也遇到了这个问题,经过多次努力后,我发现重新设计界面更容易,因此我不需要回发嵌套的模型对象。这迫使我改变我的界面工作流程:确定我现在要求用户分两步完成我梦寐以求的一步,但新方法的可用性和代码可维护性对我来说更有价值。
希望这会有所帮助。
答案 7 :(得分:1)
您可以为RenderPartial添加一个帮助器,它获取前缀并在ViewData中弹出它。
public static void RenderPartial(this HtmlHelper helper,string partialViewName, object model, string prefix)
{
helper.ViewData["__prefix"] = prefix;
helper.RenderPartial(partialViewName, model);
}
然后是另一个连接ViewData值
的帮助器 public static void GetName(this HtmlHelper helper, string name)
{
return string.Concat(helper.ViewData["__prefix"], name);
}
所以在视图中......
<% Html.RenderPartial("AnotherViewModelControl", Model.Child, "Child.") %>
在部分......
<%= Html.TextBox(Html.GetName("Name"), Model.Name) %>
答案 8 :(得分:0)
和你一样,我将Prefix属性(一个字符串)添加到我的ViewModels中,我在模型绑定输入名称之前添加它。 (YAGNI防止以下情况)
更优雅的解决方案可能是具有此属性的基本视图模型,以及一些检查视图模型是否从此基础派生的HtmlHelper,如果是,则将前缀附加到输入名称。
希望有所帮助,
丹
答案 9 :(得分:0)
在你调用RenderPartial之前怎么样
<% ViewData["Prefix"] = "Child."; %>
<% Html.RenderPartial("AnotherViewModelControl", Model.Child) %>
然后在你的部分你有
<%= Html.TextBox(ViewData["Prefix"] + "Name", Model.Name) %>
答案 10 :(得分:0)
如此处所述:https://stackoverflow.com/a/58943378/3901618 - 对于 ASP.NET Core - 您可以使用部分标签助手。
<partial name="AnotherViewModelControl" for="Child" />
<partial name="AnotherViewModelControl" for="Child2" />
它生成所有必需的名称前缀。