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?

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 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.

Method 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)
{
    //
}


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x