Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

In my view model, I have a list of objects. I iterate these objects, and create controls for each of them. In the situation below, I want to show people a textbox and a button for each object. When the user clicks the button, a post is made, and I can save the data in my controller.

In the UI, a user can change the form they want, and click save.

My problem is the model is null when it's posted to the controller..

My Razor code:

       using (Html.BeginForm())
        {
            foreach (var contributor in Model.Contributor)
            {

        @Html.HiddenFor(model => contributor.Id)

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

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

            @Html.EditorFor(model => contributor.FirstName)
            <div class="formvalidation">
                @Html.ValidationMessageFor(model => contributor.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>
            }
        }

My view model code

public class ProfileModel
{
    public string Message { get; set; }
    public List<PublisherModel> Publisher { get; set; }
    public List<ContributorModel> Contributor { get; set; }

    public ContributorModel NewContributor { get; set; }
}

My controller code

[HttpPost]
public ActionResult Mine(ProfileModel model, string newuser)
{
    //
}

How to fix it?

I guess I have to expand my view model with a way to store the changes in some way. But I really can't see how.

Right now all the properties in the ProfileModel is null when it reaches the controller.

Any ideas?

like image 406
Lars Holdgaard Avatar asked Mar 26 '13 11:03

Lars Holdgaard


1 Answers

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.

like image 106
Tomi Lammi Avatar answered Sep 26 '22 16:09

Tomi Lammi