Html.BeginForm() not passing the model

32,607

Here is a working example of what you're trying to do. This is about as a close as I can get you.

Let's start with the simplified models:

public class PlayerRow
{
    public List<AttribLine> AttribsPlayerLine { get; set; }
}

public class AttribLine
{
    public string attribName { get; set; }
    public string attribValue { get; set; }
}

Note that it is IMPORTANT to include the { get; set; } on each model property so the model binder knows it's on the block for binding.

Next is a simplified view looking only at the form() section:

@using (Html.BeginForm("Calculate", "PlayerRow", FormMethod.Post))
{
    <table>
        @*/*Build out header*/*@
        <tr>
            @foreach (AttribLine a in Model.AttribsPlayerLine)
            {
                <th>@Html.Label("title", a.attribName)</th>
            }
        </tr>
        @*/* Add row of our player attributes */*@
        <tr>
            @foreach (AttribLine a in Model.AttribsPlayerLine)
            {
                using (Html.BeginCollectionItem("AttribsPlayerLine"))
                {
                    <td>
                        @Html.TextBox("attribValue", a.attribValue)
                        @Html.Hidden("attribName", a.attribName)
                        @*
                           /* Add any more [AttribLine] properties as hidden here */
                        *@
                    </td>
                }
            }
        </tr>
    </table>
    <input class="btn-danger" type="submit" name="Next >>" value="Next >>" />
}

Note that it is IMPORTANT here to make sure that even though a property is not editable by the user, it needs to be included as a hidden element inside our CollectionItem so the model binder can SET it on the [POST]

Hope this helps.

Share:
32,607
JMon
Author by

JMon

Updated on February 26, 2020

Comments

  • JMon
    JMon about 4 years

    I have the following cshtml form:

    model Scraper.Facade.PlayerRow
    
    @using (Html.BeginForm("Calculate", "Home", FormMethod.Post))
    {
    <table class="table table-striped table-bordered table-condensed table-responsive table-hover">
        @foreach (var player in Model.AttribsPlayerLine)
        {
            <thead>
                <tr class="success">
                    @foreach (var attrib in player.AttribsPlayerList)
                    {
                        //@Html.DisplayNameFor(x => Model.tytul)
                        <th data-field="@attrib.attribName">@Html.DisplayFor(x => attrib.attribName) </th>
                    }
                </tr>
                <tr class="active">
                    @foreach (var attrib in player.AttribsPlayerList)
                    {
                        <td data-field="@attrib.attribValue">@Html.TextBoxFor(x => attrib.attribValue)</td>
                    }
                </tr>
            </thead>
    
        }
    </table>
    
    <input class="btn-danger"  type="submit" name="Next >>" value="Next >>" />
    }
    

    which is displaying correctly and then I am trying to get the model in the following controller ActionResult

    [HttpPost]
    public ActionResult Calculate(PlayerRow model)
    {
        GenericHelper _genericHelper = new GenericHelper();
        return View();
    }
    

    However the PlayerRow model is always null.

    What am I doing wrong?

    This is my model definition

    public PlayerRow LoadHtmlDoc(string fileLocation)
    {
            List<Attrib> attribsHeaderList = new List<Attrib>();
            var playerRow = new PlayerRow();
            playerRow.AttribsPlayerLine = new List<AttribLine>();
    
            var htmlDoc = new HtmlDocument { OptionFixNestedTags = true };
    
            // There are various options, set as needed
    
            // filePath is a path to a file containing the html
            htmlDoc.Load(fileLocation);
    }
    
    public class PlayerRow
    {
        public List<AttribLine> AttribsPlayerLine; 
    
    }
    

    UPDATE

    Hi All, I changed a bit the logic of my application, basically having 2 lists which has the Header Attributes, and the Attributes for all the players, so only 2 classes now, and I changed the cshtml like this, which is working now :-

    @using (Html.BeginForm("Calculate", "Home", FormMethod.Post, new { enctype =        "multipart/form-data" }))
    {
    <table class="table table-striped table-bordered table-condensed table-responsive table-hover">
        <tr>
            @for (int k = 0; k < Model.HeaderAttributes.Count; k++)
            {
                <td>
                    @Html.DisplayFor(x => x.HeaderAttributes[k].AttributeName)
                    @Html.HiddenFor(x => x.HeaderAttributes[k].AttributeName)
                </td>
            }
        </tr>
    
    
        @for (int i = 0; i < Model.PlayerList.Count; i++)
        {
            <tr>
                <td>
                    @Html.DisplayFor(x => x.PlayerList[i].PlayerName)
                    @Html.HiddenFor(x => x.PlayerList[i].PlayerName)
                </td>
                @for (int j = 0; j < Model.PlayerList[i].AttributesList.Count; j++)
                {
                    <td>
                        @Html.DisplayFor(x => x.PlayerList[i].AttributesList[j].AttributeValue)
                        @Html.HiddenFor(x => x.PlayerList[i].AttributesList[j].AttributeValue)
                    </td>
                }
            </tr>
        }
    
    </table>
    
    
    <input class="btn-danger" type="submit" name="Next >>" value="Next >>" />
    }
    

    Now my problem is, who to award the Answer, since I can say that most of the Answers were really helpful to arrive to my solution

  • JMon
    JMon about 9 years
    No luck Eli. I tried to add the following @Html.HiddenFor(model => Model.AttribsPlayerLine) and when I put a breakpoint there is data, however when I press the submit button, the model is still null. Also EditorFor did not work, still not getting any data
  • JMon
    JMon about 9 years
    Hi pnm, it is giving me an error, "Cannot resolve BeginCollectionItem
  • JMon
    JMon about 9 years
    ok added the Nuget, and did not give any problems. However the model is still null!
  • pnm
    pnm about 9 years
    looking at stackoverflow.com/questions/23436360/… sounds like restarting visual studio should fix your issue (?)
  • pnm
    pnm about 9 years
    can you add the model def to your question please?
  • JMon
    JMon about 9 years
    restarted VS but nothing new. Added my model def
  • pnm
    pnm about 9 years
    for AttribsPlayerLine, are the only two properties {attribName,attribValue}? if so try dropping that first tr tag and loop to see if that's confusing the model binder
  • JMon
    JMon about 9 years
    AttribPlayerLine is a class and has the following public List<Attrib> AttribsPlayerList = new List<Attrib>();
  • JMon
    JMon about 9 years
    and Attrib has the following :- public string attribId { get; set; } public string attribName { get; set; } public string attribValue { get; set; } public int attribValuePosMult { get; set; }
  • JMon
    JMon about 9 years
    Should I replace the Edit with the top one? Even in your edit this x => attrib.attribName is not valid since I have attrib.AttribPlayerList, so I need an indexer to get the attribName
  • Eli
    Eli about 9 years
    Hmmm that is very weird. Should definitely be giving you a partially filled data viewmodel.
  • JMon
    JMon about 9 years
    Thanks pnm, actually I did not see your solution before coming to my Answer, but I have awarded it to you since you helped me a lot to arrive to my conclusion. Thanks!