Jason
Jason

Reputation: 1226

Nested for loops - RadioButtonFor not working

I have nested foreach loops in my view, and am having problems with the radio buttons.

@foreach (var header in Model.surveyHeaders)
{
    <h1>@header.Name</h1>

foreach (var subHeader in Model.surveySubHeaders.Where(x => x.SurveryHeaderID == header.ID))
{
<h2>@subHeader.Name</h2>

    foreach (var question in Model.surveyQuestions.Where(x => x.SurveySubHeaderID == subHeader.ID))
    {
        @Html.RadioButtonFor(x => x.surveyResults.Where(y => y.SurveyQuestionID == question.ID).First().Value, 1);
        @Html.RadioButtonFor(x => x.surveyResults.Where(y => y.SurveyQuestionID == question.ID).First().Value, 2);
        @Html.RadioButtonFor(x => x.surveyResults.Where(y => y.SurveyQuestionID == question.ID).First().Value, 3);            
        @question.Question
    }
}

}

The radio button name is always 'Value', so the radio buttons are not grouped. What can I do to achieve the grouping that is desired?

Upvotes: 1

Views: 866

Answers (2)

Mathew Thompson
Mathew Thompson

Reputation: 56429

Ah collections in MVC, the joy! In order to make sure all fields are named accordingly in order to be correctly model bound on post, you need to use for loops, that will set the indexes correctly.

Before doing so, you're going to have to tweak your model structure to save your headaches. You need to re-arrange your logic so that you have a hierarchical object model in which you can iterate more cleaner (this way we're getting away from logic in the view too!)

Your survey header class, can't you put a list of subheaders on it? Then your subheader class, can you put the list of questions for that subheader? That way you can do:

@for (var i = 0; i < Model.SurveyHeaders.Count; i++)
{
    <h1>@Model.SurveyHeaders[i].Name</h1>

    for (var j = 0; j < Model.SurveyHeaders[i].SubHeaders.Count; j++)
    {
        <h2>@Model.SurveyHeaders[i].SubHeaders[j].Name</h2>

        for (var k = 0; k < Model.SurveyHeaders[i].SubHeaders[j].Questions.Count; k++)
        {
            @Html.RadioButtonFor(x => x.SurveyHeaders[i].SubHeaders[j].Questions[k].Value , 1);
            @Html.RadioButtonFor(x => x.SurveyHeaders[i].SubHeaders[j].Questions[k].Value , 2);
            @Html.RadioButtonFor(x => x.SurveyHeaders[i].SubHeaders[j].Questions[k].Value , 3);            
            @Model.SurveyHeaders[i].SubHeaders[j].Questions[k].Question
        }
    }
}

This assumes your new model structure is something like (with the following classes):

public class MyModel
{
    public List<SurveyHeader> SurveyHeaders { get; set; }
}

public class SurveyHeader
{
    public string Name { get; set; }
    public List<SubHeader> SubHeaders { get; set; }
}

public class SubHeader
{
    public string Name { get; set; }
    public List<Question> Questions { get; set; }
}

public class Question
{
    public int Value { get; set; }
    public string Question { get; set; }
}

Also one other tip, for future reference, you use the following LINQ in your code:

x => x.surveyResults.Where(y => y.SurveyQuestionID == question.ID).First().Value

You can simplify it because First can actually take a lamba, like so (although you should use FirstOrDefault and null check it in future just for safety):

x => x.surveyResults.First(y => y.SurveyQuestionID == question.ID).Value

Upvotes: 3

mreyeros
mreyeros

Reputation: 4379

You could use the overload of the RadioButtonFor extension that takes in HtmlAttributes and set the name of each of the RadioButtons.

Upvotes: 1

Related Questions