I have an app that is a multiple choice survey. The questions are loaded from a database and the answer choices are also loaded from a database. I want to bind the response to the model, but I'm not sure how to achieve this. Here is my ViewModel and View:
ViewModels:
public class SurveyViewModel : SurveyViewModelBase
{
public IEnumerable<SectionViewModel> Sections { get; set; }
public DemographicQuestionsModel DemographicQuestions { get; set; }
public string Title { get; set; }
}
public class SectionViewModel
{
public string SectionTitle { get; set; }
public IEnumerable<QuestionAnswerViewModel> QAs { get; set; }
}
public class QuestionAnswerViewModel
{
public QuestionViewModel Question { get; set; }
public AnswerViewModel SelectedAnswer { get; set; }
public IEnumerable<QuestionAnswerViewModel> ChildQuestions { get; set; }
}
public class QuestionViewModel
{
public Guid Id { get; set; }
public string Text { get; set; }
public IEnumerable<AnswerViewModel> PossibleAnswers { get; set; }
public int QuestionNumber { get; set; }
}
public class AnswerViewModel
{
public Guid Id { get; set; }
public string AnswerText { get; set; }
}
View:
@using VSC.ViewModels
@model VSC.ViewModels.SurveyViewModel
@{
SectionViewModel section = Model.Sections.FirstOrDefault();
}
<div >
<div >@(section.SectionTitle)</div>
</div>
<form action="/VSC/Survey">
@foreach (var qa in section.QAs)
{
if (qa.Question.QuestionNumber == 0)
{
<div >
@qa.Question.Text
</div>
}
else
{
if (qa.ChildQuestions != null)
{
<div parent-question":"")">
</div>
}
else
{
<div parent-question":"")">
@qa.Question.QuestionNumber @qa.Question.Text
</div>
}
<div >
@foreach (var answer in qa.Question.PossibleAnswers)
{
<div >
@{
<div >
<label>
@answer.AnswerText
@Html.RadioButtonFor(m=>qa.SelectedAnswer, answer.Id)
</label>
</div>
<div >
<label>
</label>
</div>
}
</div>
}
</div>
}
}
<input type="submit" value"Submit" />
</form>
Naturally, this produces radio buttons with all the same names, so when I click on one for a given question, the previous questions radio button unchecks and the new question is now the only one with a selection.
Any help is appreciated.
CodePudding user response:
You will need to specify the index in each ienumerable's input field. It is much better if you use for loop
, but with your current code it could be done like this;
See the code below where to create index variables, then manually create a radio button with the indexes.
@foreach (var qa in section.QAs)
{
// create the QA index
int indexQA = 0;
if (qa.Question.QuestionNumber == 0)
{
...
}
else
{
...
<div >
@foreach (var answer in qa.Question.PossibleAnswers)
{
// create possible answers index
int indexPA = 0;
<div >
@{
<div >
// create label for, with index
<label for="QA[@indexQA]PA[@indexPA]">
@answer.AnswerText
</label>
// create radio button, with index
<input type="radio" id="QA[@indexQA]PA[@indexPA]" name="Sections[0].QAs[@indexQA].Question.SelectedAnswer.Id" data-index="QA[@indexQA]" value="@answer.Id">
</div>
<div >
<label>
</label>
</div>
}
</div>
// increment possible answers index
indexPA ;
}
</div>
}
// increment QA index
indexQA ;
}
Since the radio buttons have different name
attributes, they will not be grouped with each other, so we need to make a script that unchecks the others.
@section scripts{
<script>
$(document).ready(function(){
$(".possible-answer").change(function(){
var dataValue1 = $(this).data("index");
// loop through all possible answers and check their data-index
$(".possible-answer").each(function(){
var dataValue2 = $(this).data("index");
// if they have the same index, they belong to the same group, uncheck
if(dataValue1 == dataValue2)
{
$(this).prop("checked", false);
}
});
// check the current clicked one
$(this).prop("checked", true);
});
});
</script>
}