0

I have a table:

<% using(Html.BeginForm("View2","Order"))
{  %>  
<table id="Products" class="Products">
    <tr>
        <th>ProductId</th>
        <th>Productname</th>
        <th>Quantity</th>
        <th>UnitPrice</th>
    </tr>
    <% for(int i=0; i < Model.NorthOrderDetails.Count; i++)
       {                            %>
              <tr>
        <td><%: Html.Label(Model.NorthOrderDetails[i].ProductID.ToString()) %></td>
        <td><%: Html.Label(Model.NorthOrderDetails[i].ProductName) %> </td>
        <td><%: Html.TextBoxFor(m => m.NorthOrderDetails[i].Quantity) %></td>
        <td><%: Html.TextBoxFor(m => m.NorthOrderDetails[i].UnitPrice) %></td>
          <td><button type="button" class="delete" data-id="<%:Model.NorthOrderDetails[i].ProductID %>">Delete</button><td> 
    <td><input type="hidden" name="<%:Model.NorthOrderDetails[i].ProductName %>" value="<%:i %>" /><td>
 <tr>
    <% } %>

</table>
<input type="submit" name="button" value="Add" /> 
<input type="submit" name="button" value="Save" /> 
<% } %>  

When I click the delete button I'm calling this script:

<script type="text/javascript">
          var url = '<%:Url.Action("Delete", "Order")%>';
          $('.delete').click(function () {
              var id = $(this).data('id'); // Get the product ID
              var row = $(this).closest('tr');// Get the table row
              $.post(url, { ID: id }, function () {
                  row.remove(); // remove the row from the table
                                });
          });

            </script>

And the script call this method in the Controller

[HttpPost]
public JsonResult Delete(int ID)
{
    NorthOrder forOrderDetail = (NorthOrder)Session["Order"];
    forOrderDetail.NorthOrderDetails.RemoveAll(z => z.ProductID == ID);
    Session["Order"] = forOrderDetail;

    return Json(null);
}

In the UI the row deletes correctly when I click on the submit button. But In the controller method the count of the collection equal null when I delete the first row, and it's equals to 1 when I delete the last row For example, table contains two rows when page loaded

public ActionResult View2(NorthOrder q,  string button)
{
}

Why?

C1rdec
  • 1,647
  • 1
  • 21
  • 46
user4523894
  • 143
  • 1
  • 1
  • 10
  • Do you mean _UI the row deletes correctly when I click on the **delete** button_? Why are you using session as opposed to calling the database to remove the item (your just making ajax calls, and the `Save` button will post back all other changes in one hit so it seems unnecessary). And which controller method are you referring to - `Delete()` or `View2()`? –  Mar 06 '15 at 06:55
  • OK, think I understand. I will post an answer shortly. –  Mar 06 '15 at 06:56
  • @StephenMuecke Yes. In UI the row deletes correctly when I click on the delete button. But in method View2 I get null collection even if UI contains row, and this situation appeared when I delete the first row, and then click on submit – user4523894 Mar 06 '15 at 07:15

1 Answers1

1

Because you are deleting some rows, but posting back the whole collection in the Save submit action, your indexers are either not starting a zero, or are non consecutive. In the case of deleting the 1st row, the posted values are

NorthOrderDetails[1].Quantity=SomeValue&NorthOrderDetails[1].UnitPrice=SomeOtherValue

There are no values with NorthOrderDetails[0]... so binding fails and the collection is empty.

By default, the DefaultModelBinder require collection indexers to start at zero and be non-consecutive. When you delete an item and remove its controls from the DOM, the collection cannot be bound correctly. To make this work you need to add an extra hidden input which the DefaultModelBinder uses to match up collection properties. Change the view code to

% for(int i=0; i < Model.NorthOrderDetails.Count; i++)
{%>
  <tr>
    <td><%: Html.DisplayFor(m => m.NorthOrderDetails[i].ProductID) %></td> // Remove the .ToString()
    <td><%: Html.DisplayFor(m => m.NorthOrderDetails[i].ProductName) %></td>
    <td>
      <input type="hidden" name="NorthOrderDetails.Index" value="<%: i %>" /> // add this 
      <%: Html.TextBoxFor(m => m.NorthOrderDetails[i].Quantity) %>
    </td>
    <td><%: Html.TextBoxFor(m => m.NorthOrderDetails[i].UnitPrice) %></td>
    <td><button type="button" class="delete" data-id="<%:Model.NorthOrderDetails[i].ProductID %>">Delete</button><td> 
    <td><input type="hidden" name="<%:Model.NorthOrderDetails[i].ProductName %>" value="<%:i %>" /><td>
  <tr>
<% } %>

Note: I have long forgotten aspx, but the razor code for the hidden input is <input type="hidden" name="NorthOrderDetails.Index" value="@i" /> so you may want to check my syntax.

Side notes:

  1. Your use of session seems inappropriate here. In the Delete method, you are removing the item. When your finally save the collection, you would need to get the original collection from the database and compare them to determine which items to delete in the database. Instead, you Delete() method should be calling the database to delete the item (refer my answer to your previous question)
  2. Your Add button should not be, a submit and posting to the View2() method.
Community
  • 1
  • 1
  • ProductID is int variable. I need to call ToString(). Model.NorthOrderDetails.Index. Collection doesnt contain this property – user4523894 Mar 06 '15 at 07:43
  • The helpers automatically convert any type to a 'string' so you never need to use `.ToString()`. And `Index` is not a property of your model. Its a special property used by the `DefaultModelBinder` to match up collection values. –  Mar 06 '15 at 07:48
  • And another issue which I pointed out previously is that you don't render a control for ProductID so when you save the collection, you will have no idea what's what. You need to include `Html.HiddenFor(m > m.NorthOrderDetails[i].ProductID)`. Also why do you have `Html.Label()`? Its supposed to be `Html.DisplayFor()` - which I assume is why you thought you should use `.ToString()`. A ` –  Mar 06 '15 at 07:53
  • I get Compilation Error, when I use Index, and don't use ToString() Ok. I don't use it – user4523894 Mar 06 '15 at 07:54
  • See last comment. It needs to be `Html.DisplayFor()`, not `Html.Label()` –  Mar 06 '15 at 07:55
  • Sorry, I put the aspx tags around the `name="NorthOrderDetails.Index"` - they only need to be around the `value="<%: i %>"` - see edit –  Mar 06 '15 at 08:00
  • Which one - the compilation error? If so, what are the details? –  Mar 06 '15 at 08:07
  • I get empty collection in View2(NorthOrder q, string button), after I delete the first row – user4523894 Mar 06 '15 at 08:08
  • Can you check that you are rendering `` (and another with `value="1"`). In which case it should work. What version of MVC are you using? –  Mar 06 '15 at 08:11
  • MVC4. What must I check? – user4523894 Mar 06 '15 at 08:24
  • MVC4 is OK, I meant just check that your html includes the 2 hidden inputs as per my last comment - its should look exactly as I indicated –  Mar 06 '15 at 08:27
  • You need to remove the `"Model."` bit (check the previous edit I made) –  Mar 06 '15 at 08:33
  • Can you share useful links about this topic? – user4523894 Mar 06 '15 at 08:37
  • An old one, but worth reading: [model binding to a list.](http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/) –  Mar 06 '15 at 08:49
  • You don't belive, but I again have strange problems with table – user4523894 Mar 06 '15 at 16:52
  • and problem concludes displaying data in textboxes from nondeleting item in adding item, when I deleted the firs item, after I added new item. Although, in model adding item contains null values for textboxes – user4523894 Mar 09 '15 at 11:00
  • There is nothing in your code indicating how you add an item so impossible to tell what you might be doing wrong. You need to ask a new question with the relevant code. –  Mar 09 '15 at 11:10
  • I add new question http://stackoverflow.com/questions/28940540/displaying-values-in-row-of-table-from-existing-item-in-new-row – user4523894 Mar 09 '15 at 11:11
  • No time now. Will have a detailed look tomorrow, but as I have mentioned previously STOP using session! –  Mar 09 '15 at 11:17
  • All right. I try edit myself, but your question will be important, too – user4523894 Mar 09 '15 at 11:22
  • I need session for another purposes. I deleted session, but error in new question appears – user4523894 Mar 09 '15 at 11:25