3

was trying to use Html.BeginCollectionItem to work in my application and am struggling with getting the data to post. I want to add items to a list and then post the entire list. I am using ajax and jquery to add and delete items to my list and that seems to be working. But when I post the model received in my controller is always null even though when I look at fiddler the Form Data contains all my information.

Does anyone see something simple in my code that I am doing wrong?

Main View:

@model Test.Models.TestList

@using (Html.BeginForm())
{

<div class="form-group">
    <div class="row">
        <label for="AssemblyText" class="col-sm-1 col-sm-offset-1 control-label">Assembly:</label>
        <div class="col-sm-2">
           <input type="text" id="assembly" />
        </div>
        <label for="QuantityText" class="col-sm-1 control-label">Quantity:</label>
        <div class="col-sm-2">
            <input type="text" id="Qty" />
        </div>
        <button type="button" id="AddAssembly">Add Button</button>
    </div>
</div>

<table id="Assemblies" class="table table-striped">
<thead>
    <tr>
        <th>Assembly</th>
        <th>Quantity</th>
        <th>Action</th>
    </tr>
</thead>
<tbody class="text-left">
        @if (Model != null)
        {
            foreach (var assembly in Model.mylist)
            {
                @Html.Partial("AssemblyRow", assembly)
            }
        }
</tbody>   
</table>
<div class="form-group">
    <input type="submit" id="submitbtn" class="btn btn-success" value="Submit" />
</div>
}

Partial View (AssemblyRow)

@model Test.Models.Test

<tr class="editorRow">
@using (Html.BeginCollectionItem("Assembly"))
{
<td>
    @Html.HiddenFor(m => m.assembly)
    @Html.TextBoxFor(m => m.assembly)    
</td>
<td>
    @Html.HiddenFor(m => m.Qty)
    @Html.TextBoxFor(m => m.Qty)
</td>
<td>
    <span class="dltBtn">
        <a href="#" class="deleteRow">Delete</a>
    </span>
</td>
}

My Models are simple and look like...

public class TestList
{
    public List<Test> mylist { get; set; }
}


public class Test
{
    public string assembly { get; set; }
    public string Qty { get; set; }

}

My controller

    [HttpPost]
    public ActionResult PostMain(TestList model)
    {


        return View();
    }

I can provide whatever other code you guys think is helpful but I tried to keep it simple with what I thought were the relevant pieces.

Thanks for any help!

Edit: Pic of fiddler

enter image description here

dev53
  • 404
  • 10
  • 26

3 Answers3

1

Your collection property is named mylist therefore you must pass that name to the BeginCollectionItem method

@using (Html.BeginCollectionItem("mylist"))
{
    ....

which will generate elements with name=mylist[xxxx].assembly" (where xxxx is a Guid) that are need to correctly bind to your model.

However, you have other issues with your code. The DefaultModelBinder binds the first name/value pair matching a model property and ignores any subsequent name/value pairs with the same name. Because you have a hidden input for each property before the textbox, only the initial values you sent to the view will be bound when you submit, not the edited values. You need to remove both hidden inputs s that the partial is

@model Test.Models.Test
<tr class="editorRow">
    @using (Html.BeginCollectionItem("mylist"))
    {
        <td>@Html.TextBoxFor(m => m.assembly)</td>
        <td>@Html.TextBoxFor(m => m.Qty)</td>
        <td>
            <span class="dltBtn"><a href="#" class="deleteRow">Delete</a></span>
        </td>
    }
</tr>

Side note: It is also unclear what the html in the initial <div class="form-group"> is for. You including 2 inputs and a button, but that will not bind to your model and will not correctly add items to your collection (your add button needs to use ajax to call a server method that returns another partial view and append it to the DOM)

  • This explanation helps! Thank you! I was able to get it working. – dev53 Oct 07 '16 at 14:34
  • For the benefit of searchers, if you are using a nested VM, your string might need to drill down using a dot syntax e.g. @using (Html.BeginCollectionItem("mySubClass.myList")) – JsAndDotNet Mar 21 '17 at 23:18
0

I don't see a submit button, so can't really tell what you are submitting, but try changing ActionResult PostMain(TestList model) to:

ActionResult PostMain(List<Test> model)
Andy T
  • 10,223
  • 5
  • 53
  • 95
  • I added the submit button and the form if that is helpful. Changing it to List model did not work it was still null. Thanks for the idea though! – dev53 Oct 06 '16 at 16:15
  • Could you try removing both of the `@Html.HiddenFor()`? Having the `HiddenFor()` and `TextBoxFor()` for each field is causing each field to be sent twice. – Andy T Oct 06 '16 at 16:18
  • Nope, same thing. :( – dev53 Oct 06 '16 at 16:23
  • Could you post a screenshot of the Form Data from Fiddler? – Andy T Oct 06 '16 at 16:26
  • Added! I added two elements to my list... assembly: 12 with a quantity of 1 and then another 122 with quantity of 1. – dev53 Oct 06 '16 at 16:31
  • Okay I figured it out. I did not know you had to have the name of the BeginCollectionItem the same as what you are passing to your controller. When I changed (Html.BeginCollectionItem("Assembly")) to be (Html.BeginCollectionItem("model")) it worked perfectly. Thank you for leading me to the answer. – dev53 Oct 06 '16 at 16:37
-2

You need to use Editor Template instead of partial views. Main controller context is not available in partial views. There are so many posts on Stackoverflow discussing this. One of them is ASP.NET MVC 3 - Partial vs Display Template vs Editor Template

Community
  • 1
  • 1
sam
  • 1,937
  • 1
  • 8
  • 14
  • This is wrong. OP is dynamically adding and deleting items from a collection and you cannot use an `EditorTemplate` in that case –  Oct 06 '16 at 21:20