ASP.NET binds to IEnumerable

I am passing an IEnumerable type for my view, and for each element, I output the html.textbox to inject data into.

When I post this back to my controller, the collection is empty and I don't understand why.

    public class Item
    {
        public Order Order { get; set; }
        public string Title { get; set; }
        public double Price { get; set; }
    }

      

My Get Method:

public ActionResult AddItems(Order order)
    {
        Item itemOne = new Item
        {
            Order = order
        };

        Item itemTwo = new Item
        {
            Order = order,
        };

        IList<Item> items = new List<Item> { itemOne, itemTwo };

        return View(items);
    }

      

View:

            <% int i = 0; foreach (var item in Model)
           { %>

            <p>
                <label for="Title">Item Title:</label>
                <%= Html.TextBox("items[" + i + "].Title") %>
                <%= Html.ValidationMessage("items[" + i + "].Title", "*")%>
            </p>
            <p>
                <label for="Price">Item Price:</label>
                <%= Html.TextBox("items[" + i + "].Price") %>
                <%= Html.ValidationMessage("items[" + i + "].Price", "*")%>
            </p>

        <% i++;
           } %>

      

POST method:

 [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult AddItems(IEnumerable<Item> items)
    {
        try
        {

            return RedirectToAction("Index");
        }
        catch
        {
            return View();
        }
    }

      

At the moment I have a breakpoint on the post method to check what I am getting back.

+2


source to share


3 answers


Try adding this:

<input type="hidden" name="items.Index" value="<%=i%>" />

      

Further...

I took the liberty of changing how you do your for loop so you don't have to increment your code anymore, as I always find it a little confusing. This means that you would name the current element as follows:

<%= item.Current %>

      



And you access the current loop index with:

<%= item.Index %>

      

So your view will look like this (although you never use the current element anyway, just its index):

<% foreach(var item in Model.Select((x, i) => new { Current = x, Index = i }) { %>
  <p>
    <label for="Title">Item Title:</label>
    <input type="hidden" name="items.Index" value="<%= item.Index %>" />
    <%= Html.TextBox("items[" + item.Index + "].Title") %>
    <%= Html.ValidationMessage("items[" + item.Index + "].Title", "*")%>
  </p>
  <p>
    <label for="Price">Item Price:</label>
    <%= Html.TextBox("items[" + item.Index + "].Price") %>
    <%= Html.ValidationMessage("items[" + item.Index + "].Price", "*")%>
  </p>
<% } %>

      

+1


source


The reason this doesn't work is because there is no hint as the default binding object that you want to put each of your form elements in IEnumerable.

You must make your model in the AddItems Post method match the form elements for the standard binder to set the data correctly.



Or you can just handle it directly via form variables.

0


source


I wonder if an array works for complex types with a binder, you can try doing it:

Public ActionResult AddItems(Item[] items)

      

0


source







All Articles