1

My viewmodel works fine on GET, but on post it is null and I cannot understand why. Any help would be greatly appreciated. The Quiz-post doesn't do much at this stage, I need to access the posted model before I can finish the method. The repository.GetAllQuestionsList() returns a list of QuizViewModel

ViewModel

    public class QuizViewModel {
    public int Id { get; set; }
    public int QuestionId { get; set; } 
    public string QuestionText { get; set; } 
    public int QuestionNbr { get; set; } 
    public int CurrentQuestion { get; set; } 
    public int TotalNbrOfQuestions { get; set; } 
    public List<Answer> Answers { get; set; } 
}

Controller

    public ActionResult Quiz() {
        var getAllQuestions = repository.GetAllQuestionsList();
        var model = getAllQuestions.Where(c => c.QuestionNbr == 1);
        Session["CurrentQ"] = 1;
        return View(model);
    }

    [HttpPost]
    public ActionResult Quiz(IEnumerable<Models.QuizViewModel> model) {

        int question = Convert.ToInt32(Session["CurrentQ"]);
        string str = Request.Params["btnSubmit"];
        if (str == "Next Question") {
             question++;
            Session["CurrentQ"] = question;
        }
        if (str == "Prev Question") {
             question--;
            Session["CurrentQ"] = question;
        }

       return View(model);
    }

View

@model IEnumerable<Quiz.Models.QuizViewModel>

@{
Layout = null;
}

<!DOCTYPE html>

<html>
<head>
<meta name="viewport" content="width=device-width"/>
<title>Quiz</title>
</head>

<body>
<p>
    @Html.ActionLink("Create New", "Create")
</p>
@using (Html.BeginForm()) {
<table class="table">
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.QuestionText)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.QuestionNbr)
        </th>
        <th>
            answer
        </th>
        <th></th>
    </tr>

    @foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.QuestionText)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.QuestionNbr)
            </td>
            <td>
                @foreach (var ML in item.Answers) {
                    @Html.RadioButtonFor(model => item.Answers, ML.Id)
                    @Html.Label(ML.AnswerText)
                    <br/>
                }
            </td>
        </tr>
    }

</table>
<input type="submit" id="Submit1" name="btnSubmit" value="Prev Question"/>
<input type="submit" id="Submit2" name="btnSubmit" value="Next Question"/>
}
</body>

</html>
Hansel
  • 169
  • 1
  • 8
  • Your view code doesn't make sense. If your model is an `IEnumerable` then `model.QuestionText` shouldn't work (at all!) – Liam Oct 28 '15 at 16:38
  • 2
    Possible duplicate of [Model Binding to a List MVC 4](http://stackoverflow.com/questions/15375800/model-binding-to-a-list-mvc-4) – Liam Oct 29 '15 at 08:24

2 Answers2

2

I dont't think your code will work ever as the name binding are not generated properly also @Html.DisplayNameFor(model => model.QuestionText) and @Html.DisplayNameFor(model => model.QuestionNbr) will throw exception as Model is of type IEnumerable. HttpPost binding works only if the name are input controls are generated appropriately in generated html when posted to server. I have corrected your code.Please refer the below

@{
Layout = null;
}

<!DOCTYPE html>

<html>
<head>
<meta name="viewport" content="width=device-width"/>
<title>Quiz</title>
</head>

<body>
<p>
    @Html.ActionLink("Create New", "Create")
</p>
@using (Html.BeginForm()) {
<table class="table">

       <tr>
          <th>
            @Html.DisplayNameFor(model => model[0].QuestionText)
          </th>
          <th>
            @Html.DisplayNameFor(model => model[0].QuestionNbr)
          </th>
          <th>
             answer
          </th>
           <th></th>
        </tr>
    @for (int index = 0; index > Model.Count; index++) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => modelItem[index].QuestionText)
            </td>
            <td>
                @Html.DisplayFor(modelItem => modelItem[index].QuestionNbr)
            </td>
            <td>
                @for (int jIndex = 0; jIndex < Model[index].Answers; jIndex++ ) {
                    @Html.RadioButtonFor(model => modelItem[index].Answers[jIndex].Answer, modelItem[index].Answers[jIndex].Id)
                    @Html.Label(modelItem[index].Answers[jIndex].AnswerText)
                    <br/>
                }
            </td>
        </tr>
    }

</table>
<input type="submit" id="Submit1" name="btnSubmit" value="Prev Question"/>
<input type="submit" id="Submit2" name="btnSubmit" value="Next Question"/>
}
</body>

</html>
user1672994
  • 10,509
  • 1
  • 19
  • 32
1

Ignoring the @Html.DisplayNameFor(model => model.QuestionText) which (as I said doesn't make sense). You just need to change you iterator so that it knows where in the array the item lives, i.e.

@for (int i = 0; i < Model.Count; i++) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => modelItem[i].QuestionText)
            </td>
            <td>
                @Html.DisplayFor(modelItem => modelItem[i].QuestionNbr)
            </td>
       ..... etc
}

foreach doesn't work very well in an MVC view because the HTML helper doesn't know where in the array it is and the names aren't built up correctly.

Liam
  • 27,717
  • 28
  • 128
  • 190