ASP.NET MVC vues partielles: entrée préfixes de nom

supposez que j'ai ViewModel comme

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; }
}

dans la vue je peux rendre un partiel avec

<% Html.RenderPartial("AnotherViewModelControl", Model.Child) %>

Dans le partiel je vais le faire

<%= Html.TextBox("Name", Model.Name) %>
or
<%= Html.TextBoxFor(x => x.Name) %>

cependant, le problème est que les deux vont rendre name="Name" alors que j'ai besoin d'avoir name="Child.Nom" pour que le modèle de reliure fonctionne correctement. Or, name= " Child2.Nom " quand je rends la seconde propriété en utilisant la même vue partielle.

Comment puis-je faire ma vue partielle de reconnaître automatiquement le préfixe requis? Je peux le passer en paramètre mais c'est trop gênant. C'est encore pire quand je veux par exemple le rendre récursivement. Y a-t-il un moyen de rendre des vues partielles avec un préfixe, ou, mieux encore, avec une reconception automatique de l'expression lambda appelant de sorte que

<% Html.RenderPartial("AnotherViewModelControl", Model.Child) %>

ajoutera automatiquement " enfant correct"."préfixe pour le nom généré par/id cordes?

je peux accepter n'importe quelle solution, y compris les 3-rd party view engines et libraries - j'utilise en fait Spark View Engine (Je "résous" le problème en utilisant ses macros) et MvcContrib, mais je n'y ai pas trouvé de solution. XForms, InputBuilder, MVC v2 - tout outil/insight qui fournit cette fonctionnalité sera génial.

actuellement je pense à coder cela moi-même mais il semble comme une perte de temps, Je ne peux pas croire que cette chose triviale n'est pas déjà mis en œuvre.

beaucoup de manuel solutions existe, et tous sont les bienvenus. Par exemple, je peux forcer mes partiels à être basé sur Ipalviewmodel { public string Prefix; T Model;}. Mais je préférerais une solution existante / approuvée.

mise à jour: il y a une question similaire sans réponse ici .

112
demandé sur Community 2009-09-28 23:24:24

10 réponses

vous pouvez étendre la classe Html helper par ceci:

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);

    }

et tout simplement l'utiliser dans vos vues comme ceci:

<%= Html.PartialFor(model => model.Child, "_AnotherViewModelControl") %>

et vous verrez que tout va bien!

104
répondu Mahmoud Moravej 2012-08-29 08:59:34

jusqu'à présent, je cherchais la même chose que j'ai trouvé ce post récent:

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" }
})
%>
89
répondu Jokin 2011-01-26 17:20:12

en utilisant MVC2 vous pouvez atteindre cet objectif.

Voici la vue fortement typée:

<%@ 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>

Voici la vue fortement typée pour la classe enfant (qui doit être stockée dans un sous-dossier du répertoire de vue appelé 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 />

voici le contrôleur:

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);
    }
}

Voici les classes personnalisées:

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; }
}

et la source de sortie:

<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>

Maintenant, c'est terminé. Définissez un point de rupture dans L'action Créer un contrôleur Post pour vérifier. Ne l'utilisez pas avec les listes cependant parce que cela ne fonctionnera pas. Voyez ma question sur l'utilisation de EditorTemplates avec IEnumerable pour plus sur cela.

9
répondu Nick Larsen 2009-09-28 19:51:49

Ma réponse, en fonction de la réponse de Mahmoud Moravej y compris le commentaire d'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);
    }

Edit: La réponse de Mohamoud est incorrecte pour le rendu partiel emboîté. Vous devez ajouter le nouveau préfixe de l'ancien préfixe, seulement si c'est nécessaire. Ce n'était pas clair dans les dernières réponses (:

8
répondu asoifer1879 2013-09-18 12:59:11

c'est une vieille question, mais pour quiconque arrive ici à la recherche d'une solution, envisagez d'utiliser EditorFor , comme suggéré dans un commentaire dans https://stackoverflow.com/a/29809907/456456 . Pour passer d'une vue partielle d'un éditeur de modèle, procédez comme suit.

  1. vérifiez que votre vue partielle est liée à ComplexType .

  2. déplacez votre vue partielle vers un sous-dossier EditorTemplates du dossier de vue courant, ou vers le dossier partagé . Maintenant, c'est un éditeur de modèle.

  3. changer @Html.Partial("_PartialViewName", Model.ComplexType) en @Html.EditorFor(m => m.ComplexType, "_EditorTemplateName") . L'éditeur de modèle est facultative si c'est le seul modèle pour le type complexe.

les éléments D'entrée Html seront automatiquement nommés ComplexType.Fieldname .

8
répondu R. Schreurs 2017-05-23 12:26:20

PartailFor for asp.net Core 2 au cas où quelqu'un en aurait besoin.

    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);
    }
5
répondu Rahma Sammaron 2018-08-09 14:46:53

je suis tombé sur cette question aussi et après beaucoup de peine, j'ai trouvé qu'il était plus facile de reconcevoir mes interfaces de sorte que je n'avais pas besoin de post back embeded objets model. Cela m'a forcé à changer mes flux de travail d'interface: bien sûr, je demande maintenant à l'utilisateur de faire en deux étapes ce que je rêvais de faire sur un, mais la convivialité et la maintenabilité du code de la nouvelle approche est d'une plus grande valeur pour moi maintenant.

Espérons que cela aide certains.

3
répondu Matt Kocaj 2009-09-29 07:26:13

vous pouvez ajouter un helper pour le RenderPartial qui prend le préfixe et le pop dans le ViewData.

    public static void RenderPartial(this HtmlHelper helper,string partialViewName, object model, string prefix)
    {
        helper.ViewData["__prefix"] = prefix;
        helper.RenderPartial(partialViewName, model);
    }

, Puis un autre helper qui concatène les ViewData valeur

    public static void GetName(this HtmlHelper helper, string name)
    {
        return string.Concat(helper.ViewData["__prefix"], name);
    }

et ainsi de suite ...

<% Html.RenderPartial("AnotherViewModelControl", Model.Child, "Child.") %>

dans le partiel ...

<%= Html.TextBox(Html.GetName("Name"), Model.Name) %>
1
répondu Anthony Johnston 2010-03-17 14:26:00

comme vous, j'ajoute une propriété de préfixe (une chaîne) à mes ViewModels que j'ajoute avant mon model bound input names. (YAGNI la prévention de la ci-dessous)

une solution plus élégante pourrait être un modèle de vue de base qui possède cette propriété et quelques Helpers HTML qui vérifient si le modèle de vue dérive de cette base et si oui ajoutent le préfixe au nom d'entrée.

Espère que ça aide,

Dan

0
répondu Daniel Elliott 2009-09-28 19:39:56

Que Diriez-vous juste avant que vous appelez RenderPartial vous faites

<% ViewData["Prefix"] = "Child."; %>
<% Html.RenderPartial("AnotherViewModelControl", Model.Child) %>

puis dans votre partiel vous avez

<%= Html.TextBox(ViewData["Prefix"] + "Name", Model.Name) %>
0
répondu Anthony Johnston 2009-09-28 19:47:39