ASP.NET MVC आंशिक विचार: इनपुट नाम उपसर्ग


120

मान लीजिए मेरे पास 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) %>

हालाँकि, समस्या यह है कि दोनों ही नाम = "नाम" को प्रस्तुत करेंगे जबकि मुझे मॉडल बाइंडर को ठीक से काम करने के लिए नाम = "चाइल्ड.नाम" रखने की आवश्यकता है। या, नाम = "Child2.Name" जब मैं एक ही आंशिक दृश्य का उपयोग करके दूसरी संपत्ति प्रदान करता हूं।

मैं अपने आंशिक दृश्य को आवश्यक उपसर्ग को स्वचालित रूप से कैसे पहचान सकता हूं? मैं इसे एक पैरामीटर के रूप में पारित कर सकता हूं लेकिन यह बहुत असुविधाजनक है। यह और भी बुरा है जब मैं उदाहरण के लिए इसे पुनरावर्ती रूप से प्रस्तुत करना चाहता हूं। क्या एक उपसर्ग के साथ आंशिक विचारों को प्रस्तुत करने का एक तरीका है, या, और भी बेहतर, कॉलिंग लैम्ब्डा अभिव्यक्ति के स्वचालित संयोजन के साथ ताकि

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

स्वचालित रूप से सही जोड़ देगा "बाल।" उत्पन्न नाम / आईडी तार के लिए उपसर्ग?

मैं किसी भी समाधान को स्वीकार कर सकता हूं, जिसमें 3-rd पार्टी व्यू इंजन और लाइब्रेरी शामिल हैं - मैं वास्तव में स्पार्क व्यू इंजन (मैं अपने मैक्रोज़ का उपयोग करके समस्या को हल करता हूं) और MvcContrib का उपयोग करता हूं, लेकिन वहां कोई समाधान नहीं मिला। XForms, InputBuilder, MVC v2 - इस कार्यक्षमता को प्रदान करने वाले किसी भी उपकरण / अंतर्दृष्टि बहुत अच्छा होगा।

वर्तमान में मैं इसे स्वयं कोड करने के बारे में सोचता हूं, लेकिन यह समय की बर्बादी की तरह लगता है, मैं विश्वास नहीं कर सकता कि यह तुच्छ सामान पहले से लागू नहीं है।

बहुत सारे मैनुअल समाधान मौजूद हो सकते हैं, और उन सभी का स्वागत है। उदाहरण के लिए, मैं अपने हिस्से को जबरदस्ती IPartialViewModel <T> {सार्वजनिक स्ट्रिंग उपसर्ग पर आधारित होने के लिए मजबूर कर सकता हूं; टी मॉडल; }। लेकिन मैं कुछ मौजूदा / अनुमोदित समाधान पसंद करूंगा।

अद्यतन: यहाँ कोई जवाब नहीं के साथ एक समान सवाल है

जवाबों:


110

आप इसके द्वारा 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") %>

और आप देखेंगे कि सब कुछ ठीक है!


17
यह नेस्टेड आंशिक रेंडरिंग के लिए गलत होगा। आपको पुराने उपसर्ग helper.ViewData.TemplateInfo.HtmlFieldPrefixके रूप में नए उपसर्ग को जोड़ने की आवश्यकता है{oldprefix}.{newprefix}
इवान ज़्लातेव

@Mahmoud आपका कोड बहुत अच्छा काम करता है, लेकिन मैं देख रहा था कि जब व्यूअल / व्यूबाग खाली था, तो आंशिक समय में कोड निष्पादित करने का समय आ गया था। मैंने पाया कि HtmlHelper <TModel> प्रकार के सहायक के पास एक नया प्रॉपर्टी ViewData था, जिसने बेस मॉडल को छिपा दिया था। उस के साथ, मैं के new ViewDataDictionary(helper.ViewData)साथ बदल दिया new ViewDataDictionary(((HtmlHelper)helper).ViewData)। क्या आपको इसके साथ कोई समस्या दिखाई देती है?
पैट न्यूवेल

@IvanZlatev धन्यवाद। मैं परीक्षण करने के बाद पोस्ट को सही करूंगा।
महमूद मोरवेज

2
@IvanZlatev सही है। नाम सेट करने से पहले, आपको करना चाहिएstring oldPrefix = helper.ViewData.TemplateInfo.HtmlFieldPrefix; if (oldPrefix != "") name = oldPrefix + "." + name;
kennethc

2
नेस्टेड टेम्प्लेट के लिए एक आसान निर्धारण है HtmlFieldPrefix = helper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName (नाम)
अमन महाजन

95

अब तक, मैं उसी चीज़ को खोज रहा था जो मुझे यह हालिया पोस्ट मिली है:

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
लिंक के लिए धन्यवाद, यह अब तक यहां सूचीबद्ध सबसे अच्छा विकल्प है
BZ

32
इस पार्टी के लिए सुपर देर से, लेकिन अगर आप इस दृष्टिकोण को करते हैं, तो आपको ViewDataDictionaryवर्तमान में लगने वाले कंस्ट्रक्टर का उपयोग करना चाहिए ViewData, या आप मॉडल राज्य की त्रुटियों, सत्यापन डेटा, आदि को खो देंगे
भामलिन

9
भाम्लिन की टिप्पणी पर निर्माण। आप नेस्टेड उपसर्ग के साथ भी पास कर सकते हैं जैसे (sry यह vb.net ex है): Html.RenderPartial ("एक और ViewModelControl", Model.PropX, New ViewDataDictionary (ViewData) {{.TemplateInfo = New TemplateInfo () {.tmlFieldPrefix = के साथ। ViewData.TemplateInfo.HtmlFieldPrefix & "। ​​PropX"}})
हबसन ब्रोपा

1
शानदार जवाब, +1। शायद यह @bhamlin टिप्पणी को ध्यान में रखते हुए इसे संपादित करने लायक होगा
ken2k

1
ध्यान दें कि यदि आपको एक नियंत्रक से इसे आंशिक रूप से वापस करने की आवश्यकता है, तो आपको इस उपसर्ग को वहां भी सेट करना होगा। stackoverflow.com/questions/6617768/…
मफिन मैन

12

मेरा उत्तर, इवान ज़्लाटेव की टिप्पणी सहित महमूद मोरवेज के उत्तर पर आधारित है।

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

संपादित करें: नेस्टेड आंशिक प्रतिपादन के लिए मोहम्मद का उत्तर गलत है। आपको नए उपसर्ग को पुराने उपसर्ग में संलग्न करने की आवश्यकता है, केवल अगर यह आवश्यक है। यह नवीनतम उत्तरों में स्पष्ट नहीं था:


6
यह दूसरों के लिए उपयोगी होगा यदि आप इस बारे में विस्तार से बताएंगे कि मौजूदा उत्तर पर सुधार कैसे होता है।
लीज

2
एक मोटी-उंगलियों वाली कॉपी-एंड-पेस्ट त्रुटि के बाद, यह एक ASP.NET MVC 5. +1 के लिए पूरी तरह से काम करता है।
ग्रेग बरगद

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>

अब यह पूरा हो गया है। सत्यापित करने के लिए पोस्ट कंट्रोलर एक्शन में एक ब्रेकपॉइंट सेट करें। हालांकि सूचियों के साथ इसका उपयोग न करें क्योंकि यह काम नहीं करेगा। उस पर अधिक के लिए IEnumerable के साथ EditorTemplates का उपयोग करने पर मेरा प्रश्न देखें।


हां, ऐसा दिखता है। समस्याएं यह हैं कि सूचियां काम नहीं करती हैं (जबकि नेस्टेड व्यू मॉडल आमतौर पर सूचियों में होते हैं) और यह v2 है ... जो मैं उत्पादन में उपयोग करने के लिए बिल्कुल तैयार नहीं हूं। लेकिन फिर भी यह जानने के लिए अच्छा है कि यह कुछ ऐसा होगा जिसकी मुझे आवश्यकता है ... जब यह आता है (तो +1)।
रानी ३

मैं दूसरे दिन भी आया, matthidinger.com/archive/2009/08/15/… , आप यह देखना चाहते हैं कि क्या आप यह पता लगा सकते हैं कि संपादकों के लिए इसे कैसे बढ़ाया जा सकता है (हालांकि अभी भी MVC2 में)। मैंने उस पर कुछ मिनट बिताए, लेकिन समस्याओं में भागता रहा क्योंकि मैं अभी तक भावों के अनुरूप नहीं हूं। शायद तुम मुझसे बेहतर कर सकते हो।
निक लार्सन

1
एक उपयोगी लिंक, धन्यवाद। ऐसा नहीं है कि मुझे लगता है कि EditorFor को यहाँ काम करना संभव है, क्योंकि यह [0] अनुक्रमित नहीं करता है जो मुझे लगता है (मैं शर्त लगा सकता हूँ कि यह अभी तक इसका समर्थन नहीं करता है)। एक समाधान एडिटरफोर () आउटपुट को स्ट्रिंग में रेंडर करना होगा और आउटपुट को मैन्युअल रूप से ट्विक करना होगा (आवश्यक उपसर्गों को जोड़ना)। एक गंदा हैक, हालांकि। हम्म! मैं Html.Helper () के लिए एक्सटेंशन विधि का उपयोग कर सकता हूं। UsePrefix () जो सिर्फ नाम = "x" को नाम = "prefix.x" ... MVC v1 में बदल देगा। फिर भी थोड़ा सा काम लेकिन उतना नहीं। और एचटीएमएल.प्रिफ़िक्स ("उपसर्ग")। रेंडरपार्टियल () जो जोड़ी में काम करता है।
रानी ३

Html.EditorForबच्चे के फॉर्म को रेंडर करने के लिए विधि की सिफारिश करने के लिए +1 । यह ठीक वही है जो संपादक टेम्प्लेट के लिए उपयोग किया जाना चाहिए। इसका उत्तर होना चाहिए।
ग्रेग बरगार्ड

9

यह एक पुराना प्रश्न है, लेकिन समाधान की तलाश में यहां पहुंचने वाले किसी भी व्यक्ति के लिए, उपयोग करने पर विचार करें EditorFor, जैसा कि https://stackoverflow.com/a/29809907/456456 में एक टिप्पणी में सुझाया गया है । आंशिक दृश्य से संपादक टेम्पलेट में जाने के लिए, इन चरणों का पालन करें।

  1. सत्यापित करें कि आपका आंशिक दृश्य कॉम्प्लेक्स टाइप के लिए बाध्य है ।

  2. अपने आंशिक दृश्य को वर्तमान दृश्य फ़ोल्डर के एक सबफ़ोल्डर एडिटरटेम्पलेट में ले जाएँ , या फ़ोल्डर में साझा किया गया । अब, यह एक संपादक टेम्पलेट है।

  3. बदलें @Html.Partial("_PartialViewName", Model.ComplexType)करने के लिए @Html.EditorFor(m => m.ComplexType, "_EditorTemplateName")। यदि यह जटिल प्रकार का एकमात्र टेम्पलेट है तो संपादक टेम्पलेट वैकल्पिक है।

एचटीएमएल इनपुट तत्वों को स्वचालित रूप से नाम दिया जाएगा ComplexType.Fieldname


8

यदि किसी को इसकी आवश्यकता हो तो asp.net Core 2 के लिए 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);
    }

3

मैं इस मुद्दे पर भी आया था और बहुत दर्द के बाद मैंने पाया कि मेरे इंटरफेस को फिर से डिज़ाइन करना आसान था, क्योंकि मुझे एनआईटी मॉडल ऑब्जेक्ट्स को पोस्ट करने की आवश्यकता नहीं थी। इसने मुझे अपना इंटरफ़ेस वर्कफ़्लो बदलने के लिए मजबूर किया: निश्चित रूप से मुझे अब उपयोगकर्ता को दो चरणों में करने की आवश्यकता है जो मैंने एक पर करने का सपना देखा था, लेकिन नए दृष्टिकोण की उपयोगिता और कोड स्थिरता अब मेरे लिए अधिक मूल्य की है।

आशा है कि यह कुछ मदद करता है।


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

हाँ, यही मैंने एक टिप्पणी में वर्णित है stackoverflow.com/questions/1488890/… । एक बेहतर समाधान रेंडरपार्टियल होगा जो लैम्ब्डा को भी लेता है, जिसे लागू करना आसान है। फिर भी, कोड के लिए धन्यवाद, मुझे लगता है कि सबसे दर्दनाक और कालातीत दृष्टिकोण है।
रानी ३

1
हेल्पर का उपयोग क्यों न करें। ViewData.TemplateInfo.HtmlFieldPrefix = उपसर्ग? मैं अपने सहायक में इसका उपयोग कर रहा हूं और यह ठीक काम कर रहा है।
अजेबवेन

0

आप की तरह, मैं अपने ViewModels में उपसर्ग संपत्ति (एक स्ट्रिंग) जोड़ता हूं, जिसे मैं अपने मॉडल बाध्य इनपुट नामों से पहले जोड़ता हूं। (YAGNI नीचे रोक)

एक और अधिक सुंदर समाधान एक बेस व्यू मॉडल हो सकता है जिसमें यह गुण है और कुछ HtmlHelpers हैं जो यह जांचते हैं कि क्या दृश्य मॉडल इस बेस से निकलता है और यदि ऐसा है तो उपसर्ग को इनपुट नाम में जोड़ें।

उम्मीद है की वो मदद करदे,

सज्जन


1
हम्म, बहुत बुरा। मैं कई सुंदर समाधानों की कल्पना कर सकता हूं, कस्टम HtmlHelpers के साथ गुणों, विशेषताओं, कस्टम रेंडर आदि की जांच कर रहा हूं ... लेकिन उदाहरण के लिए यदि मैं MvcContrib FluentHtml का उपयोग करता हूं, तो क्या मैं उन सभी को फिर से लिखता हूं जो मेरे हैक का समर्थन करते हैं? यह अजीब है कि कोई भी इसके बारे में बात नहीं करता है, जैसे कि हर कोई सिर्फ फ्लैट सिंगल-लेवल
व्यूमॉडल

वास्तव में, समय की किसी भी लंबाई के लिए रूपरेखा का उपयोग करने और अच्छे स्वच्छ दृश्य कोड के लिए प्रयास करने के बाद, मुझे लगता है कि थकाऊ ViewModel अपरिहार्य है। उदाहरण के लिए आदेश ViewModel के भीतर टोकरी ViewModel।
डैनियल इलियट

0

कैसे आप RenderPartial कॉल करने से ठीक पहले

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

फिर आपके आंशिक में आपके पास है

<%= Html.TextBox(ViewData["Prefix"] + "Name", Model.Name) %>

1
यह मूल रूप से इसे मैन्युअल रूप से पास करने के समान है (यह IVIVModel से "IViewModel SetPrefix (स्ट्रिंग)" के बजाय सभी दृश्य मॉडल प्राप्त करने के लिए सुरक्षित है), और बहुत बदसूरत है, जब तक कि मैं सभी एचटीएमएल सहायकों और रेंडरपार्टियल को फिर से नहीं लिखता ताकि वे स्वचालित रूप से प्रबंधित करें यह। समस्या यह नहीं है कि इसे कैसे करना है, मैंने पहले ही हल कर लिया है; समस्या यह है कि क्या यह अपने आप हो सकता है।
रानी 3

जैसा कि कोई आम जगह नहीं है, आप ऐसे कोड डाल सकते हैं जो उन सभी HTML सहायकों को प्रभावित करेंगे जिन्हें आप स्वचालित रूप से नहीं कर पाएंगे। अन्य जवाब देखें ...
एंथनी जॉनसन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.