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

그러나 문제는 둘 다 name = "Name"을 렌더링하는 반면 모델 바인더가 제대로 작동하려면 name = "Child.Name"이 필요하다는 것입니다. 또는 동일한 부분보기를 사용하여 두 번째 속성을 렌더링 할 때 name = "Child2.Name"입니다.

부분보기가 필요한 접두사를 자동으로 인식하도록하려면 어떻게합니까? 매개 변수로 전달할 수 있지만 너무 불편합니다. 예를 들어 재귀 적으로 렌더링하고 싶을 때 더 나쁩니다. 접두사를 사용하여 부분 뷰를 렌더링하는 방법이 있습니까? 아니면 호출하는 람다 식의 자동 재구성을 사용하여

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

올바른 "자식"을 자동으로 추가합니다. 생성 된 이름 / ID 문자열의 접두사?

타사 뷰 엔진 및 라이브러리를 포함한 모든 솔루션을 수락 할 수 있습니다. 실제로 Spark View Engine (매크로를 사용하여 문제를 "해결") 및 MvcContrib을 사용하지만 거기에서 솔루션을 찾지 못했습니다. XForms, InputBuilder, MVC v2-이 기능을 제공하는 모든 도구 / 인사이트는 훌륭합니다.

현재 직접 코딩을 생각하고 있지만 시간 낭비 인 것 같습니다.이 사소한 것들이 이미 구현되지 않았다는 것이 믿기지 않습니다.

많은 수동 솔루션이 존재할 수 있으며 모두 환영합니다. 예를 들어, IPartialViewModel <T> {public string Prefix; 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 귀하의 코드는 훌륭하게 작동하지만 Partial에서 코드를 실행할 때 ViewData / ViewBag가 비어 있음을 발견했습니다. HtmlHelper <TModel> 유형의 도우미에 기본 모델을 숨긴 새 속성 ViewData가 있음을 발견했습니다. 그것으로, 나는 대체 new ViewDataDictionary(helper.ViewData)와 함께 new ViewDataDictionary(((HtmlHelper)helper).ViewData). 그것에 문제가 있습니까?
Pat Newell

@IvanZlatev 감사합니다. 테스트 후 수정하겠습니다.
Mahmoud Moravej

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. 그렇지 않으면 모델 상태 오류, 유효성 검사 데이터 등이 손실됩니다.
bhamlin

9
bhamlin의 의견을 기반으로. (sry this is vb.net ex) : Html.RenderPartial ( "AnotherViewModelControl", Model.PropX, New ViewDataDictionary (ViewData) With {.TemplateInfo = New TemplateInfo () With {.HtmlFieldPrefix = ViewData.TemplateInfo.HtmlFieldPrefix & ".PropX"}})
hubson bropa

1
좋은 대답, +1. 아마 계정에 코멘트 @bhamlin 취할 편집을 가치가있을 것입니다
ken2k

1
컨트롤러에서이 부분을 반환해야하는 경우에도이 접두사를 설정해야합니다. stackoverflow.com/questions/6617768/...
머핀 맨

12

Ivan Zlatev의 의견을 포함하여 Mahmoud Moravej의 답변을 기반으로 한 내 대답입니다.

    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의 대답은 중첩 부분 렌더링에 대해 올바르지 않습니다. 필요한 경우에만 이전 접두사에 새 접두사를 추가해야합니다. 이것은 최신 답변에서 명확하지 않았습니다 (:


6
위의 내용이 기존 답변 을 어떻게 개선 하는지 자세히 설명하면 다른 사람들에게 도움이 될 것 입니다.
Leigh

2
실수로 복사하여 붙여 넣기 오류가 발생한 후이 오류는 ASP.NET MVC 5. +1에서 완벽하게 작동했습니다.
Greg Burghardt 2015

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>

이제 완료되었습니다. Create Post 컨트롤러 작업에서 중단 점을 설정하여 확인합니다. 그러나 작동하지 않기 때문에 목록과 함께 사용하지 마십시오. 이에 대한 자세한 내용은 IEnumerable과 함께 EditorTemplates 사용에 대한 내 질문을 참조하십시오.


예, 그런 것 같습니다. 문제는 목록이 작동하지 않고 (중첩 된 뷰 모델이 일반적으로 목록에있는 동안) v2라는 것입니다. 프로덕션에서 사용할 준비가되지 않았습니다. 그러나 그것이 내가 필요로 할 것이라는 것을 아는 것은 여전히 ​​좋습니다. 그것이 올 때 (그래서 +1).
queen3

저도 요 전에이 문제를 발견 했습니다. matthidinger.com/archive/2009/08/15/… , 편집자를 위해 확장하는 방법을 알아낼 수 있는지 확인하고 싶을 것입니다 (여전히 MVC2에 있음). 나는 그것에 몇 분을 보냈지 만 아직 표현과 동등하지 않기 때문에 문제가 계속 발생했습니다. 나보다 더 잘할 수 있을지도 몰라.
Nick Larsen

1
유용한 링크, 감사합니다. 내가 생각하는 [0] 인덱스를 생성하지 않기 때문에 EditorFor가 여기서 작동하도록 만드는 것이 가능하다고 생각하지 않습니다 (아직 지원하지 않을 것입니다). 한 가지 해결책은 EditorFor () 출력을 문자열로 렌더링하고 출력을 수동으로 조정하는 것입니다 (필수 접두사 추가). 그래도 더러운 해킹. 흠! MVC v1에서 name = "x"를 name = "prefix.x"...로 바꾸는 Html.Helper (). UsePrefix ()에 대한 확장 메서드를 수행 할 수 있습니다. 여전히 약간의 작업이지만 그다지 많지는 않습니다. 그리고 쌍으로 작동하는 Html.WithPrefix ( "prefix"). RenderPartial ().
queen3

+1 Html.EditorFor은 하위 양식을 렌더링 하는 방법을 추천합니다 . 이것이 바로 에디터 템플릿이 사용되어야하는 것입니다. 이것이 답이되어야합니다.
Greg Burghardt

9

이것은 오래된 질문이지만 해결책을 찾기 위해 여기에 도착하는 사람 EditorForhttps://stackoverflow.com/a/29809907/456456 의 의견에 제안 된대로를 사용 하는 것이 좋습니다 . 부분보기에서 편집기 템플릿으로 이동하려면 다음 단계를 따르세요.

  1. 부분보기가 ComplexType에 바인딩되어 있는지 확인하십시오 .

  2. 부분보기를 현재보기 폴더 의 하위 폴더 EditorTemplates 또는 Shared 폴더로 이동합니다 . 이제 편집기 템플릿입니다.

  3. 변경 @Html.Partial("_PartialViewName", Model.ComplexType)@Html.EditorFor(m => m.ComplexType, "_EditorTemplateName"). 복합 유형에 대한 유일한 템플릿 인 경우 편집기 템플릿은 선택 사항입니다.

Html 입력 요소는 자동으로 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

접두사를 가져와 ViewData에서 팝하는 RenderPartial에 대한 도우미를 추가 할 수 있습니다.

    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/… 에 대한 주석에서 설명한 내용 입니다. 더 나은 솔루션은 구현하기 쉬운 람다도 사용하는 RenderPartial입니다. 그래도 코드 덕분에 가장 고통스럽고 시간을 초월한 접근 방식이라고 생각합니다.
queen3

1
helper.ViewData.TemplateInfo.HtmlFieldPrefix = 접두사를 사용하지 않는 이유는 무엇입니까? 내 도우미에서 사용하고 있으며 잘 작동하는 것 같습니다.
ajbeaven

0

당신과 마찬가지로, 모델 바인딩 된 입력 이름 앞에 추가하는 내 ViewModels에 Prefix 속성 (문자열)을 추가합니다. (아래를 막는 야 그니)

보다 우아한 솔루션은이 속성이있는 기본보기 모델과보기 모델이이 기본에서 파생되는지 확인하고 입력 이름에 접두사를 추가하는 일부 HtmlHelpers 일 수 있습니다.

도움이 되길 바랍니다.


1
음, 너무 나쁘다. 사용자 지정 HtmlHelpers가 속성, 속성, 사용자 지정 렌더링 등을 확인하는 많은 우아한 솔루션을 상상할 수 있습니다.하지만 예를 들어 MvcContrib FluentHtml을 사용하는 경우 내 해킹을 지원하기 위해 모두 다시 작성합니까? 모두가 평평한 단일 레벨 ViewModels를 사용하는 것처럼 아무도 그것에 대해 이야기하지 않는 것이 이상합니다 ...
queen3

사실, 프레임 워크를 오랜 시간 사용하고 깔끔한 뷰 코드를 위해 노력한 후에는 계층화 된 ViewModel이 불가피하다고 생각합니다. 예를 들어 Order ViewModel 내의 Basket ViewModel.
Daniel Elliott

0

RenderPartial을 호출하기 직전에

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

그런 다음 부분적으로

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

1
이것은 기본적으로 수동으로 전달하는 것과 동일하며 (대신 "IViewModel SetPrefix (string)"을 사용하여 IViewModel에서 모든 뷰 모델을 파생하는 것이 안전합니다) 모든 Html 도우미와 RenderPartial을 다시 작성하여 자동으로 관리하지 않는 한 매우 추합니다. 이. 문제는 어떻게하는지가 아니라 이미 해결했습니다. 문제는 자동으로 할 수 있다는 것입니다.
queen3

모든 html 도우미에 영향을주는 코드를 넣을 수있는 일반적인 장소가 없기 때문에 자동으로 수행 할 수 없습니다. ... 다른 대답을 참조
안토니 존스턴에게
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.