Частичные представления ASP.NET MVC: префиксы ввода имени
Предположим, что у меня есть 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; }
}
В представлении я могу сделать частичное с
<% Html.RenderPartial("AnotherViewModelControl", Model.Child) %>
В частичном я сделаю
<%= Html.TextBox("Name", Model.Name) %>
or
<%= Html.TextBoxFor(x => x.Name) %>
Однако проблема заключается в том, что обе будут отображать name= "Name", в то время как мне нужно иметь name= "Child.Name", чтобы модельное вяжущее работало правильно. Или, name= "Child2.Name", когда я создаю второе свойство, используя тот же частичный вид.
Как сделать мой частичный вид автоматически распознанным обязательным префиксом? Я могу передать его как параметр, но это слишком неудобно. Это еще хуже, когда я хочу, например, сделать это рекурсивно. Есть ли способ рендеринга частичных представлений с префиксом или, что еще лучше, с автоматическим преобразованием вызывающего лямбда-выражения, чтобы
<% Html.RenderPartial("AnotherViewModelControl", Model.Child) %>
автоматически добавит правильный "Ребенок". префикс к сгенерированным строкам имени/идентификатора?
Я могу принять любое решение, в том числе 3-х сторонние механизмы просмотра и библиотеки - на самом деле я использую Spark View Engine (я "решаю" проблему с помощью своих макросов) и MvcContrib, но не нашел там решения. XForms, InputBuilder, MVC v2 - любой инструмент/проницательность, обеспечивающие эту функциональность, будет отличным.
В настоящее время я думаю о кодировании этого сам, но это кажется пустой тратой времени, я не могу поверить, что этот тривиальный материал уже не реализован.
Много ручных решений может существовать, и все они приветствуются. Например, я могу заставить мои частичные элементы основываться на IPartialViewModel <T> {public string Префикс; Модель Т; }. Но я предпочел бы использовать существующее/одобренное решение.
ОБНОВЛЕНИЕ: есть аналогичный вопрос без ответа здесь.
Ответы
Ответ 1
Вы можете расширить класс помощника 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") %>
и вы увидите, что все в порядке!
Ответ 2
до сих пор я искал то же самое, что нашел это последнее сообщение:
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" }
})
%>
Ответ 3
Используя 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 для проверки. Не используйте это со списками, потому что это не сработает. Дополнительную информацию см. В моем вопросе об использовании EditorTemplates с IEnumerable.
Ответ 4
Мой ответ, основанный на ответе Махмуда Моравея, включая комментарий Ивана Златева.
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 неверен для вложенного частичного рендеринга. Вам нужно добавить новый префикс к старому префиксу, только если это необходимо. Это было непонятно в последних ответах (:
Ответ 5
Это старый вопрос, но для тех, кто прибывает сюда для поиска решения, рассмотрите возможность использования EditorFor
, как это предлагается в комментарии в fooobar.com/questions/24699/.... Чтобы перейти от частичного просмотра к шаблону редактора, выполните следующие действия.
-
Убедитесь, что ваш частичный вид связан с ComplexType.
-
Переместите частичное представление в подпапку EditorTemplates текущей папки просмотра или в папку Shared. Теперь это шаблон редактора.
-
Измените @Html.Partial("_PartialViewName", Model.ComplexType)
на @Html.EditorFor(m => m.ComplexType, "_EditorTemplateName")
. Шаблон редактора является необязательным, если он является единственным шаблоном для сложного типа.
Html Элементы ввода будут автоматически называться ComplexType.Fieldname
.
Ответ 6
Я столкнулся с этой проблемой, и после большой боли я обнаружил, что было легче перепроектировать мои интерфейсы, так что мне не нужно было отправлять обратно вложенные объекты модели. Это заставило меня изменить рабочие процессы интерфейса: я теперь требую от пользователя сделать в два шага то, что я мечтал сделать на одном, но удобство использования и кода нового подхода для меня сейчас больше.
Надеюсь, это поможет некоторым.
Ответ 7
Вы можете добавить помощника для RenderPartial, который берет префикс и выталкивает его в ViewData.
public static void RenderPartial(this HtmlHelper helper,string partialViewName, object model, string prefix)
{
helper.ViewData["__prefix"] = prefix;
helper.RenderPartial(partialViewName, model);
}
Затем дополнительный помощник, который объединяет значение ViewDatap >
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
PartailFor для 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);
helper.ViewData.TemplateInfo.HtmlFieldPrefix += prefix;
return helper.Partial(partialViewName, modelExplorer.Model, helper.ViewData);
}
Ответ 9
Как и вы, я добавляю свойство Prefix (строку) к моим ViewModels, которые я добавляю перед именами, связанными с моей моделью. (YAGNI предотвращает ниже)
Более элегантным решением может быть модель базового представления, в которой есть это свойство, и некоторые HtmlHelpers, которые проверяют, выводится ли модель представления из этой базы, и если это так добавляет префикс к имени ввода.
Надеюсь, что это поможет,
Дан
Ответ 10
Как насчет того, как вы вызываете RenderPartial, вы делаете
<% ViewData["Prefix"] = "Child."; %>
<% Html.RenderPartial("AnotherViewModelControl", Model.Child) %>
Затем в частичном у вас есть
<%= Html.TextBox(ViewData["Prefix"] + "Name", Model.Name) %>