0

I'm populating an HTML table with a collection of data items.

But I want each cell to be editable. And so I'm displaying the item fields in controls. For example:

@foreach (FleetRailcarDto railcar in editSection.Railcars)
{
    <tr>
        <td></td>
        <td>
            <select asp-for="@railcar.FleetId" asp-items="@Model.FleetOptions" class="form-control-sm edit-fleet-id">
            </select>
        </td>
        <td>
            <select asp-for="@railcar.Ownership" asp-items="@Model.OwnershipOptions" class="form-control-sm edit-ownership">
            </select>
        </td>
        <td>
            <input asp-for="@railcar.CarType" class="form-control-sm edit-cartype" />
        </td>
    </tr>
}

This displays fine but it generates the same ID attributes for every table row.

I know I can build the HTML manually, but there are a few things going on here. Has anyone found a way to use asp-for for variations of the same property without causing duplicate ID attributes?

Jonathan Wood
  • 65,341
  • 71
  • 269
  • 466
  • https://stackoverflow.com/questions/15375800/model-binding-to-a-list-mvc-4 If you use the HtmlHelper functions then it will automatically generate correct html with field ids and names like "question_0_1", "question_1_1" etc `@Html.TextBoxFor(m => m.Questions[i])` – Charles Dec 31 '21 at 04:11
  • @Charles: Thanks but I'm not sure that would help. First off, it's not an array. It's an `IEnumerable<>`. Also, I'm using Razor Pages and not MVC. – Jonathan Wood Dec 31 '21 at 04:14
  • 1
    https://stackoverflow.com/a/59096797/11808788 It works the same in asp.net core, if you use the Html Helper functions (for your selects) in a for loop with index access then the correct html will be generated. – Charles Dec 31 '21 at 04:20

1 Answers1

1

When binding to a collection for editing, you should use a for loop instead of a foreach loop to iterate the collection. That way you generate an indexer to group fields together. You also need a hidden field set to the identity value of each item:

@for (var i = 0; i < editSection.Railcars.Count;i++)
{
    <tr>
        <td>
            <input type="hidden" asp=for="editSection.Railcars[i].Id" />
        </td>
        <td>
            <select asp-for="@editSection.Railcars[i].FleetId" asp-items="@Model.FleetOptions" class="form-control-sm edit-fleet-id">
            </select>
        </td>
        <td>
            <select asp-for="@editSection.Railcars[i].Ownership" asp-items="@Model.OwnershipOptions" class="form-control-sm edit-ownership">
            </select>
        </td>
        <td>
            <input asp-for="@editSection.Railcars[i].CarType" class="form-control-sm edit-cartype" />
        </td>
    </tr>
}

More info here: https://www.learnrazorpages.com/razor-pages/model-binding#binding-complex-collections

Mike Brind
  • 28,238
  • 6
  • 56
  • 88
  • The hidden field is not necessary unless you need to track the ID or other non-editable property. – Jonathan Wood Dec 31 '21 at 19:30
  • I kind of assumed you would want to track the ID so that you can determine which entry to edit in the database, but if you have got it working, that's great. – Mike Brind Dec 31 '21 at 19:31