For each loop with controls and submitting form (Html.BeginForm) in MVC 4

Solution 1:

Basically the problem is that default model binder is unable to bind collection items correctly in foreach loop. In other words, it names the element incorrectly and that's why the collection displays as null in parameters.

I'm sure there are all kinds of different workarounds, helpers and stuff but I'm not familiar with those, so I just use for loop instead of foreach, this way the elements are named correctly.

Try this:

    @for (int i = 0; i < Model.Contributor.Count(); i++)
    {

        @Html.HiddenFor(model => Model.Contributor[i].Id)

        <div class="formrow">
            @Html.ValidationSummary(true)
        </div>

        <h2>@Html.TextRaw("AuthorInfo", "Author")</h2>
        <div class="formrow">

            @Html.EditorFor(model => Model.Contributor[i].FirstName)
            <div class="formvalidation">
                @Html.ValidationMessageFor(model => Model.Contributor[i].FirstName)
            </div>
        </div>


        <div class="formrow right">
            <input type="hidden" name="formsubmitted" value="true" />
            <input type="submit"  class="button" value="@Html.Text("ButtonText", "Save")" />
        </div>
    }

I suggest you to use a debugging tool to see if elements have correct name attribute, in your case they should look like Contributor[0].Id, Contributor[0].FirstName etc.

Solution 2:

You can use PartialView for Contributor object. PartialView:

@model Contributor
using (Html.BeginForm("ContributorUpdate", "YourController"))
{
  @Html.HiddenFor(model => Model.Id)
  <div class="formrow">
    @Html.ValidationSummary(true)
  </div>

  <h2>@Html.TextRaw("AuthorInfo", "Author")</h2>
  <div class="formrow">

   @Html.EditorFor(model => Model.FirstName)
   <div class="formvalidation">
        @Html.ValidationMessageFor(model => Model.FirstName)
   </div>
</div>
<div class="formrow right">
   <input type="hidden" name="formsubmitted" value="true" />
   <input type="submit"  class="button" value="@Html.Text("ButtonText", "Save")" />
</div>
}

View will be:

@foreach (var contributor in Model.Contributor)
{
   @{Html.RenderPartial("Conributor", contributor);}
}

And controller code:

[HttpPost]
public ActionResult Mine(Conributor conributor, string newuser)
{
    //
}