Html.BeginForm() not passing the model
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.
JMon
Updated on February 26, 2020Comments
-
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 about 9 yearsNo 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 about 9 yearsHi pnm, it is giving me an error, "Cannot resolve BeginCollectionItem
-
JMon about 9 yearsok added the Nuget, and did not give any problems. However the model is still null!
-
pnm about 9 yearslooking at stackoverflow.com/questions/23436360/… sounds like restarting visual studio should fix your issue (?)
-
pnm about 9 yearscan you add the model def to your question please?
-
JMon about 9 yearsrestarted VS but nothing new. Added my model def
-
pnm about 9 yearsfor 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 about 9 yearsAttribPlayerLine is a class and has the following public List<Attrib> AttribsPlayerList = new List<Attrib>();
-
JMon about 9 yearsand 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 about 9 yearsShould 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 about 9 yearsHmmm that is very weird. Should definitely be giving you a partially filled data viewmodel.
-
JMon about 9 yearsThanks 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!