126

次のような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="Child.Name" が必要なのに対し、どちらも name="Name" をレンダリングすることです。または、同じ部分ビューを使用して 2 番目のプロパティをレンダリングすると、name="Child2.Name" になります。

部分ビューに必要なプレフィックスを自動的に認識させるにはどうすればよいですか? パラメータとして渡すことはできますが、これでは不便です。たとえば、再帰的にレンダリングしたい場合、これはさらに悪化します。接頭辞を付けて部分ビューをレンダリングする方法はありますか、または呼び出しラムダ式の自動再認識を使用して、より良い方法がありますか?

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

正しい「子」が自動的に追加されます。生成された名前/ID 文字列のプレフィックス?

サードパーティのビュー エンジンやライブラリなど、あらゆるソリューションを受け入れることができます。実際には、Spark ビュー エンジン (マクロを使用して問題を「解決」) と MvcContrib を使用していますが、解決策が見つかりませんでした。XForms、InputBuilder、MVC v2 - この機能を提供するツール/洞察はどれも優れています。

現在、これを自分でコーディングすることを考えていますが、時間の無駄のように思えます。この些細なことがまだ実装されていないとは信じられません。

多くの手動ソリューションが存在する可能性があり、それらはすべて歓迎されます。たとえば、パーシャルを強制的に IPartialViewModel<T> { public string Prefix; に基づくようにすることができます。T モデル; }。しかし、私は既存の/承認されたソリューションを好むでしょう。

更新:ここには答えのない同様の質問があります。

4

11 に答える 11

112

これにより、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") %>

すべてが問題ないことがわかります。

于 2011-06-09T11:38:35.700 に答える
98

これまでのところ、私はこの最近の投稿を見つけたのと同じものを探していました:

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" }
})
%>
于 2011-01-26T17:20:12.620 に答える
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 の答えは、ネストされた部分レンダリングでは正しくありません。必要な場合にのみ、古いプレフィックスに新しいプレフィックスを追加する必要があります。これは、最新の回答では明確ではありませんでした (:

于 2013-09-06T18:02:28.637 に答える
10

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 の使用に関する私の質問を参照してください。

于 2009-09-28T19:51:49.930 に答える
3

私もこの問題に遭遇し、多くの苦労の末、ネストされたモデルオブジェクトをポストバックする必要がないように、インターフェイスを再設計する方が簡単であることがわかりました。これにより、インターフェイスのワークフローを変更せざるを得なくなりました。確かに、私が夢見ていた 1 つのステップを 2 つのステップで実行するようにユーザーに要求するようになりましたが、新しいアプローチの使いやすさとコードの保守性は、今の私にとってより大きな価値があります。

これが役立つことを願っています。

于 2009-09-29T07:26:13.193 に答える
2

プレフィックスを取得して 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) %>
于 2009-09-29T07:59:32.307 に答える
1

あなたと同じように、モデルにバインドされた入力名の前に追加するViewModelsにPrefixプロパティ(文字列)を追加します。(YAGNIは以下を防ぎます)

より洗練された解決策は、このプロパティを持つベースビューモデルと、ビューモデルがこのベースから派生しているかどうかを確認し、派生している場合は入力名にプレフィックスを追加するHtmlHelpersです。

お役に立てば幸いです。

ダン

于 2009-09-28T19:39:56.273 に答える
1

RenderPartial を呼び出す直前に行うのはどうですか

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

次に、パーシャルで

<%= Html.TextBox(ViewData["Prefix"] + "Name", Model.Name) %>
于 2009-09-28T19:47:39.600 に答える