views:

361

answers:

3

Hi,

I'm passing a the type IEnumerable to my view, and for each item I output a html.textbox to enter the details into.

When I post this back to my controller, the collection is empty and I can't see 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);
    }

The 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++;
           } %>

The POST method:

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

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

At the moment i just have a breakpoint on the post method to check what i'm gettin back.

A: 

The reason this isn't working is because the default model binder has no clue you want to put each of your form elements into an IEnumerable.

You have to make your model in your AddItems Post method match the form elements for the default model binder to put the data in properly.

Or, you can just handle it directly through Form variables.

CubanX
Do you know where I could find any examples of what you said about matching the form elements for default binder?
A: 

I wonder if an array for complex types works with out a binder you could try making it:

Public ActionResult AddItems(Item[] items)
Michael Gattuso
No luck unfortunately :{
+1  A: 

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 that you no longer need to do the incrementing in the code, as I always find this a bit convoluted. This means that you would call the current item as:

<%= item.Current %>

And you access the current index of the loop with:

<%= item.Index %>

So your view would look like this (although you never seem to use the current item anyway, just the index of it):

<% 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>
<% } %>
Dan Atkinson
I still get an empty collection on the post action.But that way of getting the index is v nice