0

I need to create a 100% dynamic survey project where the user can create each question and each answer's type (multiple/text/radio).

My knowledge ends at the Controller because I have always build them based in a static View and a static model, but this time I don't know what will be submited in the form.

In the View I think the best choice will be including PartialViews based in the Survey's configuration inside the form.

I found a Database Model Schema has a first step:

What mysql database tables and relationships would support a Q&A survey with conditional questions?

but I can't find any example to start building the code.

Community
  • 1
  • 1
Patrick
  • 2,995
  • 14
  • 64
  • 125

2 Answers2

2

Using partial views for each question type wont work easily because by default they will not prefix the form controls name attribute with the collections indexer necessary for binding when you post and you will need multiple conditional statements to determine which partial to display.

Assuming its acceptable to group the questions in the view according to their type, then using custom EditorTemplate's for each type or for loop. A basic model structure might be

public enum QuestionType
{
    YesNo,
    Text,
    MultipleChoice
}
public class Survey
{
    public int ID { get; set; }
    public string Title { get; set; }
    public List<YesNoQuestion> YesNoQuestions { get; set; }
    public List<TextQuestion> TextQuestions { get; set; }
    public List<MultipleChoiceQuestion> MultipleChoiceQuestions { get; set; }
}
public abstract class Question
{
    public int ID { get; set; }
    public string Text { get; set; }
    public QuestionType Type { get; set; }
}
public class YesNoQuestion : Question
{
    public bool Answer { get; set; }
}
public class TextQuestion : Question
{
    public string Answer { get; set; }
}
public class MultipleChoiceQuestion : Question
{
    public int Answer { get; set; }
    public List<MultipleChoiceAnswer> PossibleAnswers { get; set; }
}
public class MultipleChoiceAnswer
{
    public int ID { get; set; }
    public int QuestionID { get; set; }
    public string Text { get; set; }
}

and a simplified view (using for loops)

@model yourAssembly.Survey
@using (Html.BeginForm())
{
  <h2>@Html.DisplayFor(m => m.Title)</h2>
  <h3>Yes/No questions</h3>
  for(int i = 0; i < Model.YesNoQuestions.Count; i++)
  {
    <div>   
      @Html.CheckBoxFor(m => m.YesNoQuestions[i].Answer)
      @Html.LabelFor(m => m.YesNoQuestions[i].Answer, Model.YesNoQuestions[i].Text)
    </div>
  }
  <h3>Text questions</h3>
  for(int i = 0; i < Model.TextQuestions.Count; i++)
  {
    @Html.LabelFor(m => m.TextQuestions[i].Answer, Model.TextQuestions[i].Text)
    @Html.TextAreaFor(m => m.TextQuestions[i].Answer)
  }
  <h3>Multiple choice questions</h3>
  for(int i = 0; i < Model.MultipleChoiceQuestions.Count; i++)
  {
    <div>@Html.DisplayFor(m => m.MultipleChoiceQuestions[i].Text)</div>
    foreach(var option in Model.MultipleChoiceQuestions[i].PossibleAnswers)
    {
      var id = string.Format("option-{0}", option.ID);
      <div>
        @Html.RadioButtonFor(m => m.MultipleChoiceQuestions[i].Answer, option.ID, new { id = id })
        <label for="@id">@option.Text</label>
      </div>
    }
  }
  <input type="submit" value="Save" />
}
  • Hi, thanks! Do you have the code for the Controller/Action to receive the form? – Patrick Jul 21 '15 at 10:03
  • @Patrick, he signature would be `public ActionResult (Survey model)` and then you would need to extract what ever information from it you need to save to the database. I guess you would need an `Answers` table with fields for the `QuestionID`, `Answer` (maybe 3 fields for bit, varchar and int to account for the different types?), `UserID` and maybe other fields such as `CreateDate` and/or `ModifyDate`. Note that the view I showed does not include inputs for properties such as `QuestionID` etc so you will need to add them as requited to suit your needs. –  Jul 21 '15 at 10:10
  • So you need to carry on everything even if you don't have a question of that type... – Patrick Jul 21 '15 at 10:15
  • Sorry, don't understand your last comment :) –  Jul 21 '15 at 10:20
  • The Survey class has to include all sort of questions even if they are not in every survey. I was thinking in a way to insert PartialViews based in a Survey configuration builded by the user, the issue is submiting it. Do you think it's a way to do it? – Patrick Jul 21 '15 at 10:22
  • As I have noted using partial views is definitely not the correct approach. Sorry, but I still don't understand your last comment and what you mean by _"based in a Survey configuration builded by the user"_ –  Jul 21 '15 at 10:25
  • So the client can build the survey questions for each step and present them to his customers so they can respond. But ok if you say it's not a good way I have to try put your code over a Database and try to put everything working. Do you have a databse model for this? – Patrick Jul 21 '15 at 10:29
  • You have not given enough information to understand exactly what you need to do, but best guess is that the client creates a new `Survey` and then creates `Questions` associated with that survey (either YesNo, Text or MultipleChoice) - or maybe selects from existing questions to add to the survey? The code I have shown is for presenting the survey and its questions to their customers so they can submit answers, not for creating the survey. –  Jul 21 '15 at 10:36
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/83853/discussion-between-patrick-and-stephen-muecke). – Patrick Jul 21 '15 at 10:37
1

I have some experience building similar products both in server side views and also in AngularJS. Whichever approach you take, your solution will look something like:

  • Create a common model for all the SurveyQuestions, define a Type argument which can be used for type-specific rendering logic
  • Create a partial view for each type of survey question with the necessary logic to render it
  • Create a "renderer" partial view which takes in a SurveyQuestion object, and then picks the right subview to render that component
  • Call RenderPartial("Renderer", SurveyQuestion) in a loop to render each type of question
  • Use controller logic to process the answers

Personally, I recommend using AngularJS (or similar) for something like this as it will simplify gluing together the different questions into a sequence; just be mindful of setting history state so users can navigate back/forward, if that's supported.

Edit - Here's a basic example of the data model and grouping logic: https://dotnetfiddle.net/4mhrxO.

Nathan Taylor
  • 24,423
  • 19
  • 99
  • 156
  • Hi, thanks! I had thought a solution using a PartialView for each question/step/survey/client and store in the Db right after the answer's selection using ajax but I found the issue that I need to put in each step (group of questions) a "send" button so the user can validate before sending and that's where I am at this point :( – Patrick Jul 20 '15 at 19:24
  • @Patrick Add a GroupId to your SurveyQuestions, `SurveyQuestions.GroupBy(x => x.GroupId)`, then call the renderer for everything in the group. – Nathan Taylor Jul 20 '15 at 19:50
  • Hi, can you share an example please? – Patrick Jul 20 '15 at 20:44