ASP.NET MVC kısmi görünümleri: giriş adı önekleri


120

ViewModel'e sahip olduğumu varsayalım.

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

Görünümde bir parçayı işleyebilirim

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

Kısmen yapacağım

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

Bununla birlikte, sorun şu ki, model bağlayıcının düzgün çalışması için name = "Child.Name" 'e ihtiyacım varken her ikisinin de name = "Name" oluşturması. Veya, ikinci özelliği aynı kısmi görünümü kullanarak oluşturduğumda name = "Child2.Name".

Kısmi görünümümün gerekli öneki otomatik olarak tanımasını nasıl sağlayabilirim? Parametre olarak iletebilirim ama bu çok sakıncalı. Örneğin onu yinelemeli hale getirmek istediğimde bu daha da kötü. Önek ile kısmi görünümler oluşturmanın bir yolu var mı, yoksa daha da iyisi, çağıran lambda ifadesinin otomatik olarak yeniden oluşturulmasıyla

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

otomatik olarak doğru "Çocuk" ekleyecektir. oluşturulan ad / kimlik dizelerine önek?

Üçüncü taraf görünüm motorları ve kitaplıklar dahil olmak üzere herhangi bir çözümü kabul edebilirim - aslında Spark View Engine (sorunu makrolarını kullanarak "çözüyorum") ve MvcContrib kullanıyorum, ancak orada bir çözüm bulamadım. XForms, InputBuilder, MVC v2 - bu işlevi sağlayan herhangi bir araç / öngörü harika olacaktır.

Şu anda bunu kendim kodlamayı düşünüyorum ama zaman kaybı gibi görünüyor, bu önemsiz şeylerin henüz uygulanmadığına inanamıyorum.

Pek çok manuel çözüm mevcut olabilir ve hepsine açığız. Örneğin, parçalarımı IPartialViewModel <T> {public string Prefix; T Modeli; }. Ancak bazı mevcut / onaylanmış çözümü tercih ederim.

GÜNCELLEME: Burada yanıtı olmayan benzer bir soru var .

Yanıtlar:


110

Html yardımcı sınıfını şu şekilde genişletebilirsiniz:

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

    }

ve aşağıdaki gibi görünümlerinizde kullanın:

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

ve her şeyin yolunda olduğunu göreceksiniz!


17
Bu, iç içe yerleştirilmiş kısmi işleme için yanlış olacaktır. Sen eski önek yeni önek gerekir helper.ViewData.TemplateInfo.HtmlFieldPrefixşeklinde{oldprefix}.{newprefix}
Ivan Zlatev

@Mahmoud Kodunuz harika çalışıyor, ancak Kısmi kod çalıştırma zamanı geldiğinde ViewData / ViewBag'in boş olduğunu buluyordum. HtmlHelper <TModel> türündeki yardımcının, temel modeli gizleyen yeni bir ViewData özelliğine sahip olduğunu buldum. Bunun üzerine, ben yerini new ViewDataDictionary(helper.ViewData)ile new ViewDataDictionary(((HtmlHelper)helper).ViewData). Bununla ilgili herhangi bir sorun görüyor musun?
Pat Newell

@IvanZlatev Teşekkürler. Gönderiyi test ettikten sonra düzelteceğim.
Mahmud Moravej

2
@IvanZlatev doğru. İsmi belirlemeden önce yapmalısınızstring oldPrefix = helper.ViewData.TemplateInfo.HtmlFieldPrefix; if (oldPrefix != "") name = oldPrefix + "." + name;
Kennethc

2
İç içe geçmiş şablonlar için kolay bir düzeltme, HtmlFieldPrefix = helper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName (name)
Aman Mahajan

95

Şimdiye kadar, bu son gönderiyi bulduğum şeyin aynısını arıyordum:

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
Bağlantı için teşekkürler, bu açık arayla burada listelenen en iyi seçenek
BZ

32
Bu partiye çok geç, ancak bu yaklaşımı yaparsanız ViewDataDictionary, akımı alan ViewData
kurucuyu

9
bhamlin'in yorumuna dayanarak. (sry, bu vb.net ex) gibi iç içe geçmiş önekleri de iletebilirsiniz: Html.RenderPartial ("AnotherViewModelControl", Model.PropX, Yeni ViewDataDictionary (ViewData) {.TemplateInfo = New TemplateInfo () With {.HtmlFieldPrefix = ViewData.TemplateInfo.HtmlFieldPrefix & ".PropX"}})
hubson bropa

1
Harika cevap, +1. Belki de @bhamlin yorumunu hesaba katmak için düzenlemeye değer olabilir
ken2k

1
Bu parçayı bir denetleyiciden iade etmeniz gerekirse, bu ön eki orada da ayarlamanız gerekeceğini unutmayın. stackoverflow.com/questions/6617768/…
The Muffin Man

12

Cevabım, Ivan Zlatev'in yorumu da dahil olmak üzere Mahmud Moravej'in cevabına dayanıyor.

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

Düzenleme: Muhammed'in cevabı iç içe geçmiş kısmi işleme için yanlış. Yeni öneki eski öneke eklemeniz gerekir, yalnızca gerekliyse. Bu son cevaplarda net değildi (:


6
Yukarıdakilerin mevcut cevaba göre nasıl geliştiğini açıklarsanız, başkalarına yardımcı olacaktır .
Leigh

2
Şişman parmaklı bir kopyala ve yapıştır hatasından sonra, bu ASP.NET MVC 5. +1 için mükemmel çalıştı.
Greg Burghardt

9

MVC2'yi kullanarak bunu başarabilirsiniz.

İşte güçlü yazılmış görünüm:

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

Alt sınıf için güçlü bir şekilde yazılmış görünüm (ki bu görünüm dizininin EditorTemplates adlı bir alt klasöründe saklanmalıdır):

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

İşte denetleyici:

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

İşte özel sınıflar:

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

Ve çıktı kaynağı:

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

Şimdi bu tamamlandı. Kayıt denetleyicisi oluştur işleminde doğrulamak için bir kesme noktası ayarlayın. Ancak bunu listelerle kullanmayın çünkü işe yaramayacaktır. Bununla ilgili daha fazla bilgi için EditorTemplates'i IEnumerable ile kullanma konusundaki soruma bakın.


Evet öyle görünüyor. Sorun, listelerin çalışmaması (iç içe geçmiş görünüm modelleri genellikle listelerde bulunurken) ve üretimde kullanmaya tam olarak hazır olmadığım v2 olması. Ama yine de ihtiyacım olan bir şey olacağını bilmek güzel ... geldiğinde (yani +1).
queen3

Geçen gün buna da rastladım, matthidinger.com/archive/2009/08/15/… , bunu editörler için nasıl genişleteceğinizi bulmak isteyebilirsiniz (hala MVC2'de olsa da). Bunun üzerine birkaç dakika harcadım, ancak problemlerle karşılaşmaya devam ettim çünkü henüz ifadelerle eşit olamadım. Belki benden daha iyisini yapabilirsin.
Nick Larsen

1
Yararlı bir bağlantı, teşekkürler. Burada EditorFor'u çalıştırmanın mümkün olduğunu düşünmüyorum, çünkü [0] indeks oluşturmayacaktır sanırım (henüz bunu desteklemediğine bahse girerim). Bir çözüm, EditorFor () çıktısını dizeye dönüştürmek ve çıktıyı manuel olarak değiştirmek (gerekli önekleri eklemek) olabilir. Yine de kirli bir hack. Hımm! Html.Helper () için uzantı yöntemi yapabilirim. UsePrefix (), MVC v1'de name = "x" yerine name = "prefix.x" koyacaktır ... Hala biraz iş ama o kadar değil. Ve Html.WithPrefix ("önek"). İkili çalışan RenderPartial ().
queen3

Alt Html.EditorForformu oluşturma yöntemini önermek için +1 . Editör şablonlarının tam olarak bunun için kullanılması gerekiyor. Cevap bu olmalı.
Greg Burghardt

9

Bu eski bir sorudur, ancak buraya gelen ve bir çözüm arayan herkes için https://stackoverflow.com/a/29809907/456456 adresindekiEditorFor bir yorumda önerildiği gibi kullanmayı düşünün . Kısmi görünümden düzenleyici şablonuna geçmek için aşağıdaki adımları izleyin.

  1. Kısmi görünümünüzün ComplexType'a bağlı olduğunu doğrulayın .

  2. Kısmi görünümünüzü , geçerli görünüm klasörünün Düzenleyici Şablonları alt klasörüne veya Paylaşılan klasörüne taşıyın . Şimdi, bir editör şablonu.

  3. Değişim @Html.Partial("_PartialViewName", Model.ComplexType)için @Html.EditorFor(m => m.ComplexType, "_EditorTemplateName"). Düzenleyici şablonu, karmaşık tür için tek şablonsa isteğe bağlıdır.

Html Giriş öğeleri otomatik olarak adlandırılacaktır ComplexType.Fieldname.


8

PartailFor birisinin ihtiyacı olması durumunda asp.net Core 2 için.

    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

Bu sorunla da karşılaştım ve çok fazla acıdan sonra arayüzlerimi iç içe geçmiş model nesnelerini geri göndermeme gerek kalmayacak şekilde yeniden tasarlamanın daha kolay olduğunu gördüm. Bu beni arabirim iş akışlarımı değiştirmeye zorladı: elbette şimdi kullanıcının tek adımda yapmayı hayal ettiğim şeyi iki adımda yapmasını zorunlu tutuyorum, ancak yeni yaklaşımın kullanılabilirliği ve kod sürdürülebilirliği şimdi benim için daha değerli.

Umarım bu biraz yardımcı olur.


1

Öneki alan ve ViewData'da açan RenderPartial için bir yardımcı ekleyebilirsiniz.

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

Ardından, ViewData değerini birleştiren başka bir yardımcı

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

ve böylece görünümde ...

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

kısmi ...

<%= Html.TextBox(Html.GetName("Name"), Model.Name) %>

Evet, stackoverflow.com/questions/1488890/… adresindeki bir yorumda anlattığım şey buydu . Daha da iyi bir çözüm, uygulaması kolay olan lambda da alan RenderPartial olacaktır. Yine de kod için teşekkürler, sanırım bu en acısız ve zamansız yaklaşım.
queen3

1
helper.ViewData.TemplateInfo.HtmlFieldPrefix = önek neden kullanılmıyor? Bunu yardımcımda kullanıyorum ve iyi çalışıyor gibi görünüyor.
ajbeaven

0

Sizin gibi, ViewModels'ime, modele bağlı girdi adlarının önüne eklediğim Prefix özelliğini (bir dize) ekliyorum. (YAGNI aşağıdakileri engelliyor)

Daha zarif bir çözüm, bu özelliğe sahip bir temel görünüm modeli ve görünüm modelinin bu temelden türetilip türetilmediğini kontrol eden ve eğer öyleyse öneki giriş adına ekleyen bazı HtmlHelpers olabilir.

Umarım yardımcı olur,

Dan


1
Hmm, çok kötü. Özel HtmlHelpers'ın özellikleri, öznitelikleri, özel oluşturmayı vb. Denetleyen birçok zarif çözümü hayal edebiliyorum ... Ancak örneğin, MvcContrib FluentHtml kullanırsam, hack'lerimi desteklemek için hepsini yeniden yazabilir miyim? Hiç kimsenin bundan bahsetmemesi garip, sanki herkes düz tek seviyeli ViewModels kullanıyor ...
queen3

Nitekim, herhangi bir süre için çerçeveyi kullandıktan ve güzel temiz görünüm kodu için çabaladıktan sonra, katmanlı ViewModel'in kaçınılmaz olduğunu düşünüyorum. Örneğin, Sipariş GörünümüModeli içindeki Sepet Görünümü Modeli.
Daniel Elliott

0

RenderPartial'ı aramadan hemen önce ararsınız

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

O zaman senin parçanda var

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

1
Bu, temelde manuel olarak geçmekle aynıdır (bunun yerine "IViewModel SetPrefix (string)" ile tüm görünüm modellerini IViewModel'den türetmek güvenlidir) ve tüm Html yardımcılarını ve RenderPartial'ı yeniden yazmadığım sürece, otomatik olarak yönetmeleri için çok çirkin. bu. Sorun nasıl yapılacağı değil, bunu zaten çözdüm; sorun şu ki, otomatik olarak yapılabilir.
queen3

tüm html yardımcılarını etkileyecek ortak bir yer olmadığından, bunu otomatik olarak yapamazsınız. diğer yanıta bakın ...
Anthony Johnston
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.