Question

Edit 3 / Final

This is the fully working example using .NET MVC, Jquery and editor templates. https://www.dropbox.com/s/t4yxbhkuowyd7va/exerciseTest.rar

Edit 2

It finally works ! Thanks to the answer below. It appears that i needed to change the NewAnswerRow partial line:

<input type='text' id='Answers_@(Model.AnswerId)__Answer'   name='Answers[@Model.AnswerId].Answer'/>

Thanks !

Edit 1:

I've followed this guide: http://www.techiesweb.net/asp-net-mvc3-dynamically-added-form-fields-model-binding/

The funny part is, that when I use his source, it works like it should. But it is almost exactly the same.

Original Post

First off all thanks for reading this message, and willing to help. I'm flabbergasted. Let me try to explain my situation. I'm trying to create a new exercise which has a list with answers. These answers are created on the fly with Jquery. I'm reading those answers with EditorTemplates. Everything works except that when i create new 'Answer' fields and press 'Post' My controller cant see any data (answers). The weird thing is. When I use data annotations like [required] in for example an answer property. I'm getting redirected to the Index. When I try it again, my model isn't empty anymore.

So basically my question is, how can i manage to get those answers into my list?

I've created a new project for you guys to be more clear and specific.

I have 2 models,

  • Exercise

  • Answers

ExerciseModel class

[Table("Exercise")]
public class ExerciseModel
{

    [Key]
    public int ExerciseId { get; set; }


    public string Question { get; set; }


    public IList<AnswerModel> Answers { get; set; }
}

It's basically a simple class. As you guys can see. There's a List with Answers.

AnswerModel

[Table("Answer")]
public class AnswerModel
{
    [Key]
    public int AnswerId { get; set; }

    public string Answer { get; set; }
 }

A simple AnswerModel class with an Id and an Answer.

My Controller

public class ExerciseController : Controller
{
    private readonly DataContext db = new DataContext();
    //
    // GET: /Exercise/
    public ActionResult Index()
    {
        var exercise = new ExerciseModel();
        exercise.Answers = GetAnswers();

        return View(exercise);
    }


    [HttpPost]
    public ActionResult Index(ExerciseModel exercisemodel)
    {
        //need to implement interaction with database (not yet) testing phase.
        return View("PostedData", exercisemodel);
    }


    public ActionResult NewAnswersRow(int id)
    {
        var answers = new AnswerModel { AnswerId = id };
        return View("partial/NewAnswersRow", answers);
    }


    private List<AnswerModel> GetAnswers()
    {
        var answerslist = new List<AnswerModel>();
        answerslist.Add(new AnswerModel { AnswerId = 1, Answer = "This is a hardcoded answer"}); //normally db.Answers (for testing purposes)
        return answerslist;
    }

}

My Index view

@model exerciseTest.Models.ExerciseModel
@using (Html.BeginForm())
{

    <div class="divAnswers">
        <div  id="container">
            <div class="editor-label">
                @Html.LabelFor(model => model.Question)
            </div>
            <div class="editor-field">
                @Html.EditorFor(model => model.Question)
                @Html.ValidationMessageFor(model => model.Question)
            </div>
            @Html.EditorFor(m => m.Answers)

        </div>
        <input type="button" id="btnAdd" value="Add New Item" />
    </div>
    <p>
        <input type="submit" value="Save" />
    </p>
}


    <script>

$(function () {
    $("#btnAdd").click(function (e) {
        var itemIndex = $("#container input.iHidden").length;
        console.debug("itemIndex : "+itemIndex);
        e.preventDefault();
        $.get("@Url.Action("NewAnswersRow", "Exercise")/"+itemIndex,function(data){
            $("#container").append(data);
        });           
    });
});

</script>

My editor template of AnswerModel

@model exerciseTest.Models.AnswerModel
@{
    Layout = null;
}
<script src="~/Scripts/jquery-1.7.1.min.js"></script>
<tr> 
 <td>
  @Html.HiddenFor(x => x.AnswerId, new { @class="iHidden" })
 @Html.TextBoxFor(x => x.Answer) </td>
</tr>

My NewAnswersRow.cshtml for creating new rows on the fly with Jquery (see Index for the code part)

@model exerciseTest.Models.AnswerModel
@{    Layout = null; }
<tr> 
 <td>
  <input type="hidden" id="Answers_@(Model.AnswerId)__Id" class="iHidden"  name='Answers[@Model.AnswerId].Id' />
  <input type='text' id='Answers_@(Model.AnswerId)__AnswersText'   name='Answers[@Model.AnswerId].AnswersText'/>
 </td>
</tr>

And my 'postedData' view for testing purposes

@model exerciseTest.Models.ExerciseModel
<h3>Posted data is </h3>

@foreach (var item in Model.Answers)
{
   <p>@item.Answer</p>
}

I'm trying to get this thing to work. I must say, In like 5 years, i've never asked someting here on StackOverflow because i could find answers. But this time, I need your help.

Thanks !

Was it helpful?

Solution

You have a bug in your NewAnswersRow partial view. Name and Id are the same in your html, however it is name that is passed in a form post. If you look at what MVC is doing, the name attribute of the answer text field is Answers[x].Answer, while the Id is Answers[x].AnswerText.

So change your name='Answers[@Model.AnswerId].AnswerText' to name='Answers[@Model.AnswerId].Answer' so it matches what MVC is expecting. Worked for me!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top