Personally, I don't like this:
enum AnswerType { String, DateTime }
I prefer to use a system like .NET. Let me offer you an alternative design. As always, we start by defining view models:
public abstract class AnswerViewModel { public string Type { get { return GetType().FullName; } } } public class StringAnswer : AnswerViewModel { [Required] public string Value { get; set; } } public class DateAnswer : AnswerViewModel { [Required] public DateTime? Value { get; set; } } public class QuestionViewModel { public int Id { get; set; } public string Caption { get; set; } public AnswerViewModel Answer { get; set; } }
then the controller:
public class HomeController : Controller { public ActionResult Index() { var model = new[] { new QuestionViewModel { Id = 1, Caption = "What is your favorite color?", Answer = new StringAnswer() }, new QuestionViewModel { Id = 1, Caption = "What is your birth date?", Answer = new DateAnswer() }, }; return View(model); } [HttpPost] public ActionResult Index(IEnumerable<QuestionViewModel> questions) {
then the main Index.cshtml view:
@model QuestionViewModel[] @using (Html.BeginForm()) { <ul> @for (int i = 0; i < Model.Length; i++) { @Html.HiddenFor(x => x[i].Answer.Type) @Html.HiddenFor(x => x[i].Id) <li> @Html.DisplayFor(x => x[i].Caption) @Html.EditorFor(x => x[i].Answer) </li> } </ul> <input type="submit" value="OK" /> }
and now we can have editor templates for our answers:
~/Views/Home/EditorTemplates/StringAnswer.cshtml :
@model StringAnswer <div>It a string answer</div> @Html.EditorFor(x => x.Value) @Html.ValidationMessageFor(x => x.Value)
~/Views/Home/EditorTemplates/DateAnswer.cshtml :
@model DateAnswer <div>It a date answer</div> @Html.EditorFor(x => x.Value) @Html.ValidationMessageFor(x => x.Value)
and the last part is a binding device for our answers:
public class AnswerModelBinder : DefaultModelBinder { protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) { var typeValue = bindingContext.ValueProvider.GetValue(bindingContext.ModelName + ".Type"); var type = Type.GetType(typeValue.AttemptedValue, true); var model = Activator.CreateInstance(type); bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, type); return model; } }
which will be registered in Application_Start :
ModelBinders.Binders.Add(typeof(AnswerViewModel), new AnswerModelBinder());