传统上,我已经建立了使用视图模型MVC应用程序与数据注释属性,我动态地呈现使用编辑器模板的意见。 一切的伟大工程,它确实减少了需要我来构建新的视图的时间。 我的要求最近更改。 现在,我不能定义在设计时视图模型。 这将在视图上呈现的特性是在基于业务规则的运行时间决定。 此外,对于那些性质的验证规则可以在运行时决定为好。 (可在我看来,需要未在我的域模型需要一个领域,根据业务规则)。 同样,将被渲染是未知的,直到运行时属性集 - 用户A可以编辑从模型6点的特性,而用户B可以编辑9个属性。
我想知道是否有可能创建一个从业务规则提供自己的元数据像属性名称和值的集合非类型化的视图模型的模型元数据提供商。 有没有人解决这个问题呢?
我通过创建一个更复杂的模型,并使用自定义编辑模板,使模型呈现看起来像一个典型的编辑器,但使用动态字段的信息解决了类似的问题:
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
一系列答案车型。 每个答案模型类型都有它自己的编辑模板,让我来定制不同类型的动态“特性”应如何显示。 例如:
// 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,它可以绑定在每一个每个答案类型的属性没有任何困难。
显然,我省略了很多细节,比如如何生产的回答模型摆在首位,但希望这让你在正确的轨道上。
你可以使用的ViewData属性,你的视图模型,视图和控制器,它是动态的,所以它可以在运行时解决。