3

従来、私はデータ注釈属性を持つビュー モデルを使用して MVC アプリケーションを構築し、エディター テンプレートを使用してビューを動的にレンダリングしてきました。すべてがうまく機能し、新しいビューを作成するのにかかる時間を大幅に短縮できます。私の要件は最近変更されました。現在、デザイン時にビュー モデルを定義できません。ビューにレンダリングされるプロパティは、ビジネス ルールに基づいて実行時に決定されます。また、これらのプロパティの検証規則も実行時に決定される場合があります。(私のドメイン モデルでは必須ではないフィールドが、ビジネス ルールに基づく私の見解では必須である可能性があります)。また、レンダリングされるプロパティのセットは実行時までわかりません。ユーザー A はモデルから 6 つのプロパティを編集でき、ユーザー B は 9 つのプロパティを編集できます。

プロパティ名と値のコレクションのような型指定されていないビュー モデルのビジネス ルールから独自のメタデータを提供するモデル メタデータ プロバイダーを作成できるかどうか疑問に思っています。誰かがこの問題を解決しましたか?

4

2 に答える 2

6

より複雑なモデルを作成し、カスタム エディター テンプレートを使用してモデルを典型的なエディターのようにレンダリングしますが、動的フィールド情報を使用することで、同様の問題を解決しました。

public class SingleRowFieldAnswerForm
{
    /// <summary>
    /// The fields answers to display.
    /// This is a collection because we ask the MVC to bind parameters to it,
    /// and it could cause issues if the underlying objects were being recreated
    /// each time it got iterated over.
    /// </summary>
    public ICollection<IFieldAnswerModel> FieldAnswers { get; set; } 
}

public interface IFieldAnswerModel
{
    int FieldId { get; set; }

    string FieldTitle { get; set; }

    bool DisplayAsInput { get; }

    bool IsRequired { get; }

    bool HideSurroundingHtml { get; }
}

// sample implementation of IFieldAnswerModel
public class TextAreaFieldAnswer : FieldAnswerModelBase<TextAreaDisplayerOptions>
{
    public string Answer { get; set; }
}

EditorTemplates/SingleRowFieldAnswerForm.cshtml:

@helper DisplayerOrEditor(IFieldAnswerModel answer)
{
    var templateName = "FieldAnswers/" + answer.GetType().Name;
    var htmlFieldName = string.Format("Answers[{0}]", answer.FieldId);

    if (answer.DisplayAsInput)
    {
        @Html.EditorFor(m => answer, templateName, htmlFieldName)
        // This will display validation messages that apply to the entire answer. 
        // This typically means that the input got past client-side validation and
        // was caught on the server instead.
        // Each answer's view must also produce a validation message for
        // its individual properties if you want client-side validation to be
        // enabled.
        @Html.ValidationMessage(htmlFieldName)
    }
    else
    {
        @Html.DisplayFor(m => answer, templateName, htmlFieldName)
    }
}
<div class="form-section">
    <table class="form-table">
        <tbody>
            @{
                foreach (var answer in Model.FieldAnswers)
                {
                    if (answer.HideSurroundingHtml)
                    {
                        @DisplayerOrEditor(answer)
                    }
                    else
                    {
                        var labelClass = answer.IsRequired ? "form-label required" : "form-label";
                        <tr>
                            <td class="@labelClass">
                                @answer.FieldTitle:
                            </td>
                            <td class="form-field">
                                <div>
                                    @DisplayerOrEditor(answer)
                                </div>
                            </td>
                        </tr>
                    }
                }
            }
        </tbody>
    </table>
</div>

SingleRowFieldAnswerFormそのため、一連の回答モデルをmy に入力します。各回答モデル タイプには独自のエディター テンプレートがあり、さまざまなタイプの動的な「プロパティ」の表示方法をカスタマイズできます。例えば:

// EditorTemplates/FieldAnswers/TextAreaFieldAnswer.cshtml

@model TextAreaFieldAnswer

@{
    var htmlAttributes = Html.GetUnobtrusiveValidationAttributes("Answer", ViewData.ModelMetadata);
    // add custom classes that you want to apply to your inputs.
    htmlAttributes.Add("class", "multi-line input-field");
}
@Html.TextAreaFor(m => m.Answer, Model.Options.Rows, 0, htmlAttributes)
@Html.ValidationMessage("Answer")

次のトリッキーな部分は、この情報をサーバーに送信するときに、どのタイプのIFieldAnswerModelを構築するかを本質的に認識していないためSingleRowAnswerForm、引数リストで をバインドすることはできません。代わりに、次のようにする必要があります。

public ActionResult SaveForm(int formId)
{
    SingleRowAnswerForm form = GetForm(formId);
    foreach (var fieldAnswerModel in form.FieldAnswers.Where(a => a.DisplayAsInput))
    {
        // Updating this as a dynamic makes sure all the properties are bound regardless
        // of the runtime type (since UpdateModel relies on the generic type normally).
        this.TryUpdateModel((dynamic) fieldAnswerModel, 
            string.Format("Answers[{1}]", fieldAnswerModel.FieldId));
        }
    ...

バインドする各動的「プロパティ」値を MVC に提供したため、各回答タイプの各プロパティを問題なくバインドできます。

明らかに、最初に回答モデルを作成する方法など、多くの詳細を省略しましたが、うまくいけば、これで正しい軌道に乗ることができます。

于 2012-05-31T18:16:59.467 に答える
1

ViewModel、View、および Controller で ViewData プロパティを使用できます。これは動的であるため、実行時に解決できます。

于 2012-05-31T17:02:02.610 に答える