より複雑なモデルを作成し、カスタム エディター テンプレートを使用してモデルを典型的なエディターのようにレンダリングしますが、動的フィールド情報を使用することで、同様の問題を解決しました。
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 に提供したため、各回答タイプの各プロパティを問題なくバインドできます。
明らかに、最初に回答モデルを作成する方法など、多くの詳細を省略しましたが、うまくいけば、これで正しい軌道に乗ることができます。