asp.net-mvc - parciales - vista parcial en modal mvc
Vistas parciales MVC de ASP.NET: prefijos de nombre de entrada (10)
¿Qué tal si justo antes de llamar a RenderPartial lo haces?
<% ViewData["Prefix"] = "Child."; %>
<% Html.RenderPartial("AnotherViewModelControl", Model.Child) %>
Entonces en tu parcial tienes
<%= Html.TextBox(ViewData["Prefix"] + "Name", Model.Name) %>
Supongamos que tengo ViewModel como
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; }
}
En la vista puedo renderizar un parcial con
<% Html.RenderPartial("AnotherViewModelControl", Model.Child) %>
En el parcial lo haré
<%= Html.TextBox("Name", Model.Name) %>
or
<%= Html.TextBoxFor(x => x.Name) %>
Sin embargo, el problema es que ambos representarán name = "Name" mientras que necesito tener name = "Child.Name" para que la carpeta de modelo funcione correctamente. O bien, name = "Child2.Name" cuando represento la segunda propiedad utilizando la misma vista parcial.
¿Cómo hago para que mi vista parcial reconozca automáticamente el prefijo requerido? Puedo pasarlo como un parámetro, pero esto es demasiado inconveniente. Esto es aún peor cuando quiero, por ejemplo, renderizarlo recursivamente. ¿Hay alguna forma de renderizar vistas parciales con un prefijo o, mejor aún, con una reconexión automática de la expresión lambda que llama para que
<% Html.RenderPartial("AnotherViewModelControl", Model.Child) %>
automáticamente agregará el correcto "Niño". prefijo a las cadenas de nombre / id generadas?
Puedo aceptar cualquier solución, incluidos los motores y bibliotecas de vistas de terceros. De hecho, utilizo Spark View Engine ("soluciono" el problema usando sus macros) y MvcContrib, pero no encontré una solución allí. XForms, InputBuilder, MVC v2: cualquier herramienta / visión que brinde esta funcionalidad será excelente.
Actualmente, pienso en programar esto por mi mismo, pero parece una pérdida de tiempo, no puedo creer que estas cosas triviales no se hayan implementado.
Pueden existir muchas soluciones manuales, y todas son bienvenidas. Por ejemplo, puedo forzar que mis parciales se basen en IPartialViewModel <T> {public string Prefix; Modelo T; }. Pero prefiero una solución existente / aprobada.
ACTUALIZACIÓN: hay una pregunta similar sin respuesta here .
Al igual que usted, agrego la propiedad Prefijo (una cadena) a mis ViewModels que agrego antes de los nombres de entrada de mi modelo. (YAGNI previene el siguiente)
Una solución más elegante podría ser un modelo de vista base que tenga esta propiedad y algunos HtmlHelpers que verifiquen si el modelo de vista deriva de esta base y, si es así, añada el prefijo al nombre de la entrada.
Espero que ayude,
Dan
Esta es una vieja pregunta, pero para cualquiera que llegue aquí buscando una solución, considere usar EditorFor
, como se sugiere en un comentario en https://.com/a/29809907/456456 . Para pasar de una vista parcial a una plantilla de editor, siga estos pasos.
Verifique que su vista parcial esté vinculada a ComplexType .
Mueva su vista parcial a una subcarpeta EditorTemplates de la carpeta de vista actual, oa la carpeta Shared . Ahora, es una plantilla de editor.
Cambie
@Html.Partial("_PartialViewName", Model.ComplexType)
a@Html.EditorFor(m => m.ComplexType, "_EditorTemplateName")
. La plantilla del editor es opcional si es la única plantilla para el tipo complejo.
Los elementos de entrada HTML se denominarán automáticamente ComplexType.Fieldname
.
Me encontré con este problema también y después de mucho dolor encontré que era más fácil rediseñar mis interfaces de modo que no necesitaba publicar objetos de modelo anidados. Esto me obligó a cambiar mis flujos de trabajo de interfaz: seguro que ahora le pido al usuario que haga en dos pasos lo que soñé hacer en uno, pero la usabilidad y la mantenibilidad del código del nuevo enfoque es de mayor valor para mí ahora.
Espero que esto ayude a algunos.
Mi respuesta, basada en la respuesta de Mahmoud Moravej, incluye el comentario de 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);
}
Editar: La respuesta de Mohamoud es incorrecta para la representación parcial anidada. Debe agregar el nuevo prefijo al prefijo anterior, solo si es necesario. Esto no estaba claro en las últimas respuestas (:
Puede agregar un ayudante para RenderPartial que toma el prefijo y lo abre en ViewData.
public static void RenderPartial(this HtmlHelper helper,string partialViewName, object model, string prefix)
{
helper.ViewData["__prefix"] = prefix;
helper.RenderPartial(partialViewName, model);
}
Luego, un asistente adicional que concatena el valor de ViewData
public static void GetName(this HtmlHelper helper, string name)
{
return string.Concat(helper.ViewData["__prefix"], name);
}
y entonces en la vista ...
<% Html.RenderPartial("AnotherViewModelControl", Model.Child, "Child.") %>
en el parcial ...
<%= Html.TextBox(Html.GetName("Name"), Model.Name) %>
Puede extender la clase Html helper de esta manera:
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);
}
y simplemente úsalo en tus vistas de esta manera:
<%= Html.PartialFor(model => model.Child, "_AnotherViewModelControl") %>
y verás que todo está bien!
Usando MVC2 puedes lograr esto.
Aquí está la vista fuertemente tipada:
<%@ 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>
Aquí está la vista fuertemente tipada para la clase secundaria (que debe almacenarse en una subcarpeta del directorio de vista llamada 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 />
Aquí está el controlador:
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);
}
}
Estas son las clases personalizadas:
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; }
}
Y la fuente de salida:
<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>
Ahora esto está completo. Establezca un punto de interrupción en la acción del controlador Create Post para verificar. No use esto con listas, sin embargo, porque no funcionará. Consulte mi pregunta sobre el uso de EditorTemplates con IEnumerable para obtener más información al respecto.
hasta ahora, estaba buscando lo mismo que encontré en esta publicación reciente:
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" }
})
%>
PartailFor para asp.net Core 2 en caso de que alguien lo necesite.
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);
}