0

I have an quiz application which has questions with multiple answers. The problem is that I don't have an idea how to send the answered options to the controller to check if they are correct? So far, I have made a form with multiple check boxes but I am confused which type should the controllers method receive?

Here is the form:

 @using (Html.BeginForm("CheckAnswers", "Questions", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">

        @foreach (var item in Model.OfferedAnswers)
        {

            <hr />
            <div class="form-group">
                @Html.HiddenFor(modelItem => item.ID)
                @Html.HiddenFor(modelItem=>item.QuestionID)
                @Html.HiddenFor(modelItem=>item.AnswerID)
                @Html.LabelFor(modelItem => item.Answer.text, "AnswerID", htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.CheckBox("IsCorrect", false, htmlAttributes: new { @class = "form-control" })
                    @Html.ValidationMessageFor(modelItem => item.Answer.text, "", new { @class = "text-danger" })
                </div>
            </div>
        }


        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Провери" class="btn btn-default" />
            </div>
        </div>
    </div>
}

Here is the code of the controller method:

   public ActionResult CheckAnswers(int ID, int QuestionID, int AnswerID, bool isCorrect)
    {

        OfferedAswer oa = db.OfferedAnswers.Find(ID);

        return RedirectToAction("Index");
    }

This may work for single choice answers. I am new to .net mvc . Thanks in advance.

green
  • 393
  • 1
  • 5
  • 18
  • You cannot use a `foreach` loop to generate form controls for collections. Refer [this answer](http://stackoverflow.com/questions/30094047/html-table-to-ado-net-datatable/30094943#30094943) –  Jun 21 '16 at 22:48

2 Answers2

1

Well, first, you need to accept something as a param to your action method that the posted values can be bound to. For example:

public ActionResult CheckAnswers(List<QuestionAnswerViewModel> model)

Where QuestionAnswerViewModel would have the 4 params you've currently got there as properties.

Here, though, it would be best to just accept the same model you're using in your view. That way, the posted values will natural bind back to where they came from.

Then, in your view, you need to use for rather than foreach, so you end up with indexed field names:

    @for (var i = 0; i < Model.OfferedAnswers.Count(); i++)
    {

        <hr />
        <div class="form-group">
            @Html.HiddenFor(m => m.OfferedAnswers[i].ID)
            @Html.HiddenFor(m => m.OfferedAnswers[i].QuestionID)
            @Html.HiddenFor(m => m.OfferedAnswers[i].AnswerID)
            @Html.LabelFor(m => m.OfferedAnswers[i].Answer.text, "AnswerID", htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.CheckBoxFor(m => m.OfferedAnswers[i], false, htmlAttributes: new { @class = "form-control" })
                @Html.ValidationMessageFor(m => m.OfferedAnswers[i].Answer.text, "", new { @class = "text-danger" })
            </div>
        </div>
    }
Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
0

You can use Editor templates.

Assuming your view model for your GET view looks like this

public class QuestionViewModel
{
  public int Id {set;get;}
  public string QuestionText { set;get;}
  public List<Answer> OffererdAnswers {set;get;}
}
public class Answer
{
  public int Id {set;get;}
  public string AnswerText {set;get;}
  public bool IsSelected { set;get;}
}

Now, in your GET action, you are loading the OffererdAnswers collection of a QuestionViewModel object and sending that to the view

public ActionResult Question()
{
  var vm = new QuestionViewModel { Id=2, QuestionText="What is your dream"};
  vm.OfferedAnswers = new List<Answer>{
       new Answer { Id=1, AnswerText="Get a job" },
       new Answer { Id=2, AnswerText="Buy a car" },
       new Answer { Id=3, AnswerText="Buy a boat" },
  };
  return View(vm);
}

Now create an editor template in ~/Views/YourCurrentControllerDirectory/EditorTemplates. The file name should be Answer.cshtml ( same name as the type we are going to use for). Paste this code to that file

@model YourNamespaceHere.Answer
<div>
    @Html.CheckBoxFor(s => s.IsSelected)
    @Html.HiddenFor(s=>s.Id)
    @Html.LabelFor(s=>s.AnswerText)
</div>

Now in your main view, which is strongly typed to the QuestionViewModel,simply use the EditorFor helper method.

@model YourNamespaceHere.QuestionViewModel
<h2>@Model.QuestionText</h2>
@using(Html.BeginForm())
{
  @Html.HiddenFor(s=>s.Id)
  @Html.EditorFor(s=>s.OfferedAnswers)
  <input type="submit"/>
}

You can keep the same type(QuestionViewModel) as the parameter type for your HttpPost action method. When the form is posted, check the OfferedAnswers collection and filter the one's with IsSelected set to true.

[HttpPost]
public ActionResult Question(QuestionViewModel model)
{
  var answers = model.OfferedAnswers.Where(s=>s.IsSelected);
  // to do return something.
}
Shyju
  • 214,206
  • 104
  • 411
  • 497