I have a FeedbackViewModel that contains a list of questions:
public class FeedbackViewModel
{
public List<QuestionViewModel> Questions { get; set; }
}
This QuestionViewModel is an object that can be inherited by 5 different types of questions
public class QuestionViewModel
{
public string QuestionText { get; set; }
public string QuestionType { get; set; }
}
An example of one of the inheriting question types:
public class SingleQuestionViewModel : QuestionViewModel
{
public string AnswerText { get; set; }
}
In the HttpGet
of the Index
action in the controller I get the questions from the database and add the correct question type in list of question in the FeedbackViewModel
Then I render this model in the view:
@using (Html.BeginForm())
{
//foreach (var item in Model.Questions)
for (int i = 0; i < Model.Questions.Count; i++)
{
<div class="form-group">
@Html.DisplayFor(modelItem => Model.Questions[i].QuestionText, new { @class = "control-label col-md-4" })
<div class="col-md-6">
@if (Model.Questions[i].QuestionType == "Single")
{
@Html.EditorFor(modelItem => (Model.Questions[i] as OpenDataPortal.ViewModels.SingleQuestionViewModel).AnswerText)
}
else if (Model.Questions[i].QuestionType == "Multiple")
{
@Html.TextAreaFor(modelItem => (Model.Questions[i] as OpenDataPortal.ViewModels.SingleQuestionViewModel).AnswerText)
}
else if (Model.Questions[i].QuestionType == "SingleSelection")
{
@Html.RadioButtonForSelectList(modelItem => (Model.Questions[i] as OpenDataPortal.ViewModels.SingleSelectionQuestionViewModel).SelectedAnswer,
(Model.Questions[i] as OpenDataPortal.ViewModels.SingleSelectionQuestionViewModel).SelectionAnswers)
}
else if (Model.Questions[i].QuestionType == "MultipleSelection")
{
@Html.CustomCheckBoxList((Model.Questions[i] as OpenDataPortal.ViewModels.MultipleSelectionQuestionViewModel).AvailableAnswers)
}
else if (Model.Questions[i].QuestionType == "UrlReferrer")
{
@Html.EditorFor(modelItem => (Model.Questions[i] as OpenDataPortal.ViewModels.SingleQuestionViewModel).AnswerText)
}
</div>
</div>
<br />
}
<br />
<button type="submit">Submit</button>
}
Now, I simply can't get it to post the list of questions in the model. Is it even possible to post a list of different object types?
Edit: Following is the list of data within the post that I discovered using Fiddler:
Thanks for pointing me in the right direction with this post. I was struggling to get the syntax right for binding a non-sequential
IDictionary<string, bool>
object. Not sure this is 100% correct, but this Razor code worked for me:If you need a checkbox, be sure to use Html.CheckBox instead of a standard HTML checkbox. The model will blow up if a value is not provided, and Html.CheckBox generates a hidden field to ensure a value is present when the checkbox is not checked.
Make sure you are rendering your view in order so that
Model.Questions[i]
renders in order.For example,
Model.Questions[0], Model.Questions[1], Model.Questions[2]
. I noticed that if the order is not correct mvc model binder will only bind the first element.After much research I've found two solutions:
One is to write HTML that has hardcoded Id's and Names Two is to convert your ICollection/IEnumerable to an Array or List (i.e IList something with an 'index'), and have an Array object in your BindingModel in your Controller POST Action.
Thanks to Phil Haack's (@haacked) 2008 blog post http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/ Which is still relevant to how the default ModelBinder works today for MVC. (NB: the links in Phil's article to sample porject and extension methods are broken)
HTML snippet that inspired me:
Post array looks a bit like:
Model:
Controller GET:
Edit.cshtml:
Form is processed in the Controller:
Post Model:
Controller:
I use this code maybe its can help
Using Razor you can implement the for loop using a dictionary as follows without making changes to your object:
The collection needs to be either a List or Array for this to work.