1

I am working on an ASP.NET MVC application which allows users to create their own surveys. For the sake of simplicity in answering this question, lets say that they can only create surveys with multiple-choice questions, each of which will be displayed to a survey-taker as a question followed by a list of radiobuttons representing each possible answer. Users can create as many multiple-choice questions in a survey as they want.

So, I have a ViewModel as follows:

public class SurveyViewModel
{
    public int Id { get; set; }
    public IList<Question> Questions { get; set; }
}

public class Question
{
    public int Id { get; set; }
    public string Question { get; set; }
    public IList<Answer> Answers { get; set; }
}

public class Answer
{
    public int Id { get; set; }
    public string LabelText { get; set; }
}

Usually, I would just add a field for each question in the form to the SurveyViewModel. However, in this case I obviously don't know how many questions exist because the user can create however many they want - so I can't create a field to store the user's answer for each question.

So my question is: How can I write the ViewModel and the View (in Razor syntax) so that I can submit a form to a Controller, such that the responses to each of the arbitrary number of multiple-choice questions can be saved?

I have spent a long time banging my head against the wall on this one, so all help is much appreciated! Thank you!

1
  • Hi, I should clarify - we assume the questions have been created previously. I'm only interested in the bit where we display the question to a survey-taker. Commented Dec 15, 2015 at 15:40

1 Answer 1

3

Not sure exactly what you're asking because it seems you have everything you need, or at least most of what you need. In your view, you would just iterate over Questions on your view model, and then for each question, iterate over Answers. The only thing to bear in mind is that you need to use a for loop rather than foreach so you can index the list properties, allowing Razor to generate the right input names for the modelbinder. The one modification you need to make is to add a property like the following to Question:

public int SelectedAnswerId { get; set; }

Then, your view would look something like:

@for (var i = 0; i < Model.Questions.Count(); i++)
{
    <p>@Model.Questions[i].Question</p>

    @for (var j = 0; j < Model.Questions[i].Answers.Count(); j++)
    {
        @Html.RadioButtonFor(m => m.Questions[i].SelectedAnswerId, Model.Questions[i].Answers[j].Id, new { id = "Question" + i.ToString() + "Answer" + j.ToString() })
        <label for="Question@(i)Answer@(j)">@Model.Questions[i].Answers[j].LabelText</label>
    }
}

EDIT: Corrected radio button labels by passing an id for each each radio button and creating a label using that id. Otherwise, you'd end up with a bunch of labels that wouldn't actually activate the appropriate radio button.

Sign up to request clarification or add additional context in comments.

5 Comments

Ah thanks that does the trick! The only thing is my question was a simplified version of what I'm actually doing... Because there might be other types of questions (e.g. one where you type the answer into a textbox) I'm having to cast each item in Questions to different subclasses of an abstract Question class. I can't figure out how you can get this to work with the casting?? Is it possible? I realise this is a horribly messy way of doing things but as hard as I've thought about it, I can't find a way to do this without all the casting. Thanks.
Actually I thought it worked, but it doesnt. When I get my form back in the Controller the Questions is null... Is there something else needed in that code?
Nope. That's all you should need. As long as Questions is properly indexed and at least one property on it is posted (in other words, you have a least one input on your page with a name like Questions[0].Foo), then Questions should not be null.
As for the different question types, you'll need to utilize a view model for your individual questions and condense it down into a least common denominator representation of a "question". You can't post back multiple different types of subclasses, because the modelbinder will not be able to differentiate.
OK, I got it to work - it was just the access modifiers on the setters for the ViewModel grr. Thanks for the help though!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.