How to bind selected values from dropdownlists in a dynamically generated table? ASP.NET Core MVC
Asked Answered
E

1

6

In my ASP.NET Core MVC project there is a table with two columns containing dropdownlists where the user is supposed to pick rates values and click save to update them for each row in the table. There are dozens of dynamically generated rows in total, which results in twice as many dropdownlists with values to track.

Again, the goal is to update each row with the values selected in the two dropdownlists in that row.

I was able to create the table and display the values. There is a loop that generates each row in the table. The issue I am having is not knowing how to pass all of the values from all of the dropdownlists back to the controller along with rownumbers so I can write the logic to update the values in the database. I can pass a single dropdown combination from the first row and I don't know how to do the same for all of them or indicate which row they are from. The fact that dropdownlists are in a loop makes this difficult.

This is the part of the view where I display the table:

      <tbody>
      @foreach (var item in Model.WorksheetRates)
      {
      var recordNumber = PagedContext.Page * PagedContext.PageSize + Model.WorksheetRates.IndexOf(item) + 1;
      <tr>
           <td>@recordNumber</td>
           <td>
               @item.Program
           </td>
           <td>
               @item.Area
           </td>
            <td>
                @Html.DropDownListFor(x => x.GARateID, gaRatesList, new { @style = "width:280px;" })
            </td>
             <td>
                 @Html.DropDownListFor(x => x.OverheadRateID, overheadRatesList, new { @style = "width:280px;" })
             </td>
         </tr>
      }
                        </tbody>

This is the model:

public class BudgetPrepViewModel
{
    public List<WorksheetRate> WorksheetRates { get; set; }

    public int GARateID { get; set; }
    public int OverheadRateID { get; set; }

    public IEnumerable<SelectListItem> GARateList { get; set; }
    public IEnumerable<SelectListItem> OverheadRateList { get; set; }
}

As you can see I added IEnumerable<SelectListItem> object because I thought they would need to be used and iterated in the controller. This is a guess, I don't know how to bind to them. And it's important I associate them with the correct row.

This is what I came up with in the controller for the header, the actual code inside is not relevant to the question:

[HttpPost, ActionName("SelectRates")]
public IActionResult SelectRates([Bind("GARateID, OverheadRateID")] BudgetPrepViewModel data)
{
        BPViewModel model = new BPViewModel
        {
            MonthNameList = MonthSelectList(),
            PrepStep = 8
        };

       return View("Index", model);
}

Any help would be appreciated. Please let me know if something is not clear, I did my best to describe this question. Thank you!

Emogeneemollient answered 19/11, 2021 at 6:30 Comment(2)
Do the rows need to be saved all at the same time, or when the rate is picked? I think you should change your approach. But before, I need to know if the rows can be treated independently.Tourney
@Tourney Thank you for the answer. The requirement is that all of the rows are saved at the same time. Could you please elaborate on how you think the approach should be changed?Emogeneemollient
T
2

First of all, I'm assuming that WorksheetRate has GARateID and OverheadRateID as properties, since they are inside the same table row. Secondly, I've added a property Id to the WorksheetRate so that the selected options could be identified when the form is submitted. That said, now the classes are like this:

public class WorksheetRate
{
    public int Id { get; set; }
    public string Program { get; set; }
    public string Area { get; set; }
    public int GARateID { get; set; }
    public int OverheadRateID { get; set; }
}
public class BudgetPrepViewModel
{
    public List<WorksheetRate> WorksheetRates { get; set; }

    public IEnumerable<SelectListItem> GARateList { get; set; }
    public IEnumerable<SelectListItem> OverheadRateList { get; set; }
}

The key aspect here is that the name of the selects and inputs must follow a pattern so that ASP.NET bind it to the model. For this case, to bind values to the property GARateID from BudgetPrepViewModel.WorksheetRates the name of selects and inputs must be:

  • selects:
    • <select name="WorksheetRates[0].GARateID">
    • <select name="WorksheetRates[1].GARateID">
  • inputs
    • <input hidden name="WorksheetRates[0].Id" />
    • <input hidden name="WorksheetRates[1].Id" />

Therefore, the solution is:

@for (var i=0; i<Model.WorksheetRates.Count(); i++)
{
    <tr>
        <td>
            @Model.WorksheetRates[i].Id
            <input hidden asp-for="WorksheetRates[i].Id" /> 
        </td>
        <td>
            @Model.WorksheetRates[i].Program
        </td>
        <td>
            @Model.WorksheetRates[i].Area
        </td>
        <td>
            @Html.DropDownListFor(x => x.WorksheetRates[i].GARateID, Model.GARateList, new { @style = "width:280px;" })
        </td>
        <td>
            @Html.DropDownListFor(x => x.WorksheetRates[i].OverheadRateID, Model.OverheadRateList, new { @style = "width:280px;" })
        </td>
    </tr>
}

I have created a repository with the full working solution.

Trudi answered 21/11, 2021 at 17:16 Comment(2)
Thank you very much for your response. I will need some time to go through you answer and I will get back to you.Emogeneemollient
Thank you for your answer. It was very clear and helpful.Emogeneemollient

© 2022 - 2024 — McMap. All rights reserved.