18

I am trying to get my view to post a List back to the action however it keeps coming in as null.

So my Model has a List of WeightEntry objects.

Exercise Model

public class Exercise
{
    public List<WeightEntry> Entries { get; set; }
    public int ExerciseID { get; set; }
    public int ExerciseName { get; set; }
}

WeightEntry Model

public class WeightEntry
{
    public int ID { get; set; }
    public int Weight { get; set; }
    public int Repetition { get; set; }
}

My View contains the ExerciseName and a forloop of WeightEntry objects

@model Mymvc.ViewModels.Exercise
...
<span>@Model.ExerciseName</span>
@using (Html.BeginForm())
{
    @Html.ValidationSummary(true)
    <table class="left weight-record">
        <tr>
            <th>Reps</th>
            <th>Weight</th>
        </tr>
        @foreach (var item in Model.Entries)
        {
            <tr>
                <td>
                    @Html.EditorFor(x => item.Repetition)
                </td>
                <td>
                    @Html.EditorFor(x => item.Weight)
                </td>
            </tr>
        }
    </table>
    <input type="submit" value="Save" /> 
}

The Controller Action (Post) Does nothing at the moment. I am just trying to get the binding working before I add the save code.

[HttpPost]
public ActionResult WeightEntry(Exercise exercise)
{
    try
    {
        //Add code here to save and check isvalid    
        return View(exercise);
    }
    catch
    {
        return View(exercise);
    }
}

I have seen a few little tricks with adding a numerator to the form elements' names used in MVC2 but I was wondering if MVC3 was any different? I was hoping it would all bind nicely with ID's being 0 or null but instead the whole List is null when I inspect it after the form posts. Any help is appreciated. Thanks.

1 Answer 1

41

Replace the following loop:

@foreach (var item in Model.Entries)
{
    <tr>
        <td>
            @Html.EditorFor(x => item.Repetition)
         </td>
         <td>
             @Html.EditorFor(x => item.Weight)
         </td>
     </tr>
}

with:

@for (var i = 0; i < Model.Entries.Count; i++)
{
    <tr>
        <td>
            @Html.EditorFor(x => x.Entries[i].Repetition)
         </td>
         <td>
             @Html.EditorFor(x => x.Entries[i].Weight)
         </td>
     </tr>
}

or even better, use editor templates and replace the loop with:

@Html.EditorFor(x => x.Entries)

and then define a custom editor template that will automatically be rendered for each element of the Entries collection (~/Views/Shared/EditorTemplates/WeightEntry.cshtml):

@model WeightEntry
<tr>
    <td>
        @Html.EditorFor(x => x.Repetition)
     </td>
     <td>
         @Html.EditorFor(x => x.Weight)
     </td>
 </tr>

The the generated input elements will have correct names and you will be able to successfully fetch them back in your POST action.

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

3 Comments

Why does the foreach end up returning null for the collection though?
@CodeBlend, look at the name attributes of the input fields in the generated HTML and compare it in both cases. Then read the following article and you will understand why it works with for and it doesn't work with foreach.
I personally like keeping the html in one file for readability... especially in this case where the template is simply 2 td's.

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.