I'm trying to create a survey page that can have textboxes and lists of radio buttons or checkbox fields. No matter what I try, I can't get model.Questions property to bind when the form is submitted; the model is created with a null Questions property.
Please tell me you have some idea that can help me!
The view model looks like this:
// Survey view model
public class Question
{
public int Id { set; get; }
public string QuestionText { set; get; }
public bool IsHeading { get; set; }
public bool IsList { get; set; }
public bool IsCheckBox { get; set; }
public List<Answer> Answers { set; get; }
[Required]
public string SelectedAnswer { set; get; }
public string GivenAnswer { get; set; }
public Question()
{
Answers = new List<Answer>();
GivenAnswer = "-1";
SelectedAnswer = "-1";
QuestionText = "";
}
}
public class Answer
{
public int Id { set; get; }
public string AnswerText { set; get; }
}
public class Assessment
{
public int Id { get; set; }
public string Name { get; set; }
public int LessonId { get; set; }
public Module Lesson { get; set; }
public SurveyType SurveyType { get; set; }
public virtual List<Question> Questions { set; get; }
public Assessment()
{
Questions = new List<Question>();
}
}
The controller method looks like this:
[HttpPost]
public ActionResult Survey(Assessment model)
{
if (ModelState.IsValid)
{
foreach (var q in model.Questions)
{
var qId = q.Id;
var selectedAnswer = q.SelectedAnswer;
// Save the data
}
return RedirectToAction("ThankYou", new { id = model.Id }); //PRG Pattern
}
//to do : reload questions and answers
return View(model);
}
Where Assessment
is the main view model. Below are the view (Survey.cshtml) and Editor template (EditorTemplates/Question.cshtml, for each Question)
Survey.cshtml:
@model ZTTDD.Models.Assessment
@using ZTTDD.Models
<div class="row">
<div class="col-md-9">
@using (Html.BeginForm())
{
@Html.HiddenFor(m => m.Id)
@Html.EditorFor(m => m.Questions)
<input type="submit" />
}
</div>
<div class="col-md-3">
<p>
@Html.ActionLink((string)ViewBag.LessonLinkBackText, "Lesson", "Lessons", new { id = Model.Lesson.Id }, null)
</p>
<p>
@Html.ActionLink(linkName, linkMethod, "Lessons", new { id = Model.Lesson.Id }, null)
</p>
</div>
</div>
EditorTemplates/Question.cshtml
@model ZTTDD.Models.Question
@if (Model.IsHeading)
{
<div class="survey-heading">
<h4>@Model.QuestionText</h4>
</div>
<hr/>
}
@if (!Model.IsHeading)
{
<div class="form-group">
@Html.HiddenFor(m => m.Id)
<p> @Model.QuestionText </p>
@if (Model.IsList)
{
<div class="radio">
@Html.HiddenFor(m => m.GivenAnswer)
@foreach (var a in Model.Answers)
{
if (Model.IsCheckBox)
{
<span class="form-horizontal">
@Html.CheckBox(String.Format("Questions_{0}__SelectedAnswer", Model.Id), false, new { value = "", name = String.Format("Questions[{0}].SelectedAnswer", Model.Id) }) @Html.Raw(a.AnswerText)
</span>
}
else
{
@Html.RadioButtonFor(b => b.SelectedAnswer, a.Id) @Html.Raw(a.AnswerText) <br />
}
}
</div>
}
@if (!Model.IsList)
{
Model.GivenAnswer = "";
@Html.HiddenFor(m => m.SelectedAnswer)
@Html.TextAreaFor(m => m.GivenAnswer)
}
</div>
}
Here's a list of the keys that get posted:
[0] "Id" string
[1] "Questions[1].Id" string
[2] "Questions[1].GivenAnswer" string
[3] "Questions[1].SelectedAnswer" string
[4] "Questions[2].Id" string
[5] "Questions[2].GivenAnswer" string
[6] "Questions[2].SelectedAnswer" string
[7] "Questions[3].Id" string
[8] "Questions[3].GivenAnswer" string
[9] "Questions[3].SelectedAnswer" string
[10] "Questions[5].Id" string
[11] "Questions[5].GivenAnswer" string
[12] "Questions[5].SelectedAnswer" string
[13] "Questions[6].Id" string
[14] "Questions[6].GivenAnswer" string
[15] "Questions[6].SelectedAnswer" string
[16] "Questions[7].Id" string
[17] "Questions[7].GivenAnswer" string
[18] "Questions[7].SelectedAnswer" string
[19] "Questions[9].Id" string
[20] "Questions[9].SelectedAnswer" string
[21] "Questions[9].GivenAnswer" string
[22] "Questions[10].Id" string
[23] "Questions[10].SelectedAnswer" string
[24] "Questions[10].GivenAnswer" string
UPDATE:
Just a little clarification: A Question object can represent a heading (no fields associated) or input (either radio buttons, checkboxes or textarea). As far as I can tell, the values I need are posting properly. I'll try to grab them from the debugger and post here.
UPDATE:
This querystring below is from Request.Form.ToString(), with url-decoding and formatting for easier reading. As you can see, the values are posting, but for some reason are not getting bound to Assessment.Questions. Could this in fact be due to the xx value in Questions[xx] because it's not an index value, but an actual Id?
Id=1&
Questions[1].Id=2&
Questions[1].GivenAnswer=-1&
Questions[1].SelectedAnswer=2&
Questions[2].Id=3&
Questions[2].GivenAnswer=-1&
Questions[2].SelectedAnswer=5&
Questions[3].Id=5&
Questions[3].GivenAnswer=-1&
Questions[3].SelectedAnswer=8&
Questions[5].Id=7&
Questions[5].GivenAnswer=-1&
Questions[5].SelectedAnswer=12&
Questions[6].Id=8&
Questions[6].GivenAnswer=-1&
Questions[6].SelectedAnswer=15&
Questions[7].Id=9&
Questions[7].GivenAnswer=-1&
Questions[7].SelectedAnswer=18&
Questions[9].Id=11&
Questions[9].SelectedAnswer=-1&
Questions[9].GivenAnswer=sdfg sdfg dsfg&
Questions[10].Id=12&
Questions[10].SelectedAnswer=-1&
Questions[10].GivenAnswer=sdfg dfsg sdfg
LAST UPDATE:
The issue was the fact that I wasn't posting anything back to the server for Questions that are headings so the Questions index was in fact incomplete. Adding a hidden Id field with each heading fixed the problem.