3
votes

I have a standard Razor Page that lists all Orders in my database. It's coded pretty standard: IList<Order> bind property in page model, OnPost method. However, each of the records in a table has a column with a select element that shows the current status of the order, and a button to save the new status if the user decides to change it. Sort of an inline edit of the status for each row.

Form Preview

So I have a foreach loop in my page that cycles through all my orders:

@foreach (var item in Model.Orders)
{
   <tr><td>...</td></tr>
   <tr>
      <td>
         <form method="post" class="form-inline">
            <input type="hidden" value="@item.Id"/>
            <select asp-for="@item.OrderStatusId"
                    class="form-control form-control-sm"
                    asp-items="ViewBag.OrderStatusId"></select>
            <button type="submit" class="btn btn-secondary btn-sm">
               <i class="fas fa-save"></i>
            </button>
         </form>
      </td>
   </tr>
}

As you can see, the last column is a form. What I want done is to be able to submit to my OnPost method, the order id (hidden input element in the form) and the selected state (select in element the form). However, these parameters always show up as null in the method. I'm guessing it has something to do with the fact that, because there are multiple asp-for elements for the same property name (one for each form - each order/row), Razor gets confused and doesn't know which one to send.

Is there a way around this?

2

2 Answers

1
votes

The foreach loop in your example generates same name attribute for <select> elements, which explains why Razor got confused (and it's also invalid HTML). Assumed that Model.Orders implements IEnumerable, you should use a for loop and numeric index for every iteration to generate different <select> element names:

<form method="post" class="form-inline">
@for (int i = 0; i < Model.Orders.Count; i++)
{
   <tr><td>...</td></tr>
   <tr>
      <td>
            <input type="hidden" value="@item.Id"/>
            <select asp-for="Model.Orders[i].OrderStatusId" class="form-control form-control-sm"
                    asp-items="ViewBag.OrderStatusId"></select>
      </td>
   </tr>
}
   <button type="submit" class="btn btn-secondary btn-sm">
      <i class="fas fa-save">Save</i>
   </button>
</form>

Note: I intentionally put the <form> & submit <button> tags outside the loop because it's unnecessary to create multiple <form> tags (and multiple submit buttons) without setting action attribute inside for loop, unless specified otherwise.

Similar issue: How can I bind an array with asp-for tag?

1
votes

The select tag helper will automatically generate a multi-select if the property specified in the asp-for attribute is an IEnumerable.

public class OrderModel
{
     public IEnumerable<string> OrderStatusId{ get; set; }
}

Binding the asp-for attribute to the OrderStatusId property as follows:

<select asp-for="OrderStatusId" 
    asp-items="ViewBag.OrderStatus">
</select>