'How to Bind a List in Model with HTML Form in ASP.NET Core?

I'm unsure what I'm doing wrong when trying to map form values to my model, FieldMappingCollection. I've been able to get the Id and Name back for the model but not the list of mappings, either the previously existing or newly created ones.

The page contains a text box for the mapping name and a table of rows which contains a select list and a text box. The FromField relates to the select list and the ToField relates to the text box.

I'm not super familiar with ASP.NET Core and even less familiar with older versions, so I'm unsure if @Html... is totally phased out or what the proper syntax is for automating binding or how to implement custom binding or HTML generators etc. I'm also not concerned with exactly how I should be handling the select list in particular, it's a bit of temporary code to get the page working and I'll come back to it later.

public class FieldMappingCollection
{
    public int Id { get; set; }
    public string Name { get; set; } = "";

    public List<FieldMapping> FieldMappings { get; set; } = new();
}

public class FieldMapping
{
    public int Id { get; set; }

    public string FromField { get; set; } = "";
    public string ToField { get; set; } = "";
}
public class MyController
{
    public static List<string> AvailableFields = new()
    {
        // predefined field names for select list...
    };

    private readonly IMappingRepository m_repo;

    // constructor, Index, etc...

    public IActionResult EditMappingCollection(int id)
    {
        return View(m_repo.GetById(id));
    }

    [HttpPost]
    public IActionResult EditMappingCollection(FieldMappingCollection model)
    {
        m_repo.Update(model);
        m_repo.Save();

        return RedirectToAction(nameof(Index));
    }
}
@model FieldMappingCollection

// other required stuff...

<form asp-action="EditMappingCollection" method="post">
    <input hidden asp-for="Id" type="number" />
    <input type="text" asp-for="Name" />
    <table class="table">
        <thead>
            <tr>
                <th scope="col">From Field</th>
                <th scope="col">To Field</th>
            </tr>
        </thead>
        <tbody>
            @for (int i = 0; i < Model.FieldMappings.Count; i++)
            {
                <tr>
                    // Nothing to store the mapping Id yet
                    <td><select asp-for="FieldMappings[@i].FromField" style="width: 100%;" asp-items="@MyController.AvailableFields.Select(field => new SelectListItem(field, field, Model.FieldMappings[i].FromField == field))"></select></td>
                    <td><input asp-for="FieldMappings[@i].ToField" style="width: 100%;" type="text" placeholder="To Field" value="@Model.FieldMappings[i].ToField" /></td>
                </tr>
            }
        </tbody>
    </table>
    <input class="btn btn-primary" type="submit" value="Save Changes" />
</form>



Solution 1:[1]

You could read the offcial document how to bind collection target: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/model-binding?view=aspnetcore-6.0 I tried as below:

public class FieldMappingCollection
    {
        public FieldMappingCollection()
        {
            Selectlist = new List<SelectListItem>();
        }
       ........   
        
        public List<SelectListItem> Selectlist { get; set; }
    }

In controller I tried?

public IActionResult EditMappingCollection()
        {
            var FieldMapping1 = new FieldMapping() { Id = 1, FromField = "fromfield1" };
            var FieldMapping2 = new FieldMapping() { Id = 2, FromField = "fromfield2" };

            var FieldMappingCollection = new FieldMappingCollection();

            FieldMappingCollection.Selectlist = new List<SelectListItem>()
            {
                new SelectListItem() { Text= FieldMapping1.FromField,Value=FieldMapping1.Id.ToString() },
                 new SelectListItem() { Text= FieldMapping2.FromField,Value=FieldMapping2.Id.ToString() }
            };
            return View(FieldMappingCollection);
        }

In view?

<table class="table">
    <thead>
        <tr>
            <th scope="col">From Field</th>
            <th scope="col">To Field</th>
        </tr>
    </thead>
    <tbody>

        <select name=FieldMappings[0].FromField class="form-control" asp-items="@Model.Selectlist"></select>
        <input name=FieldMappings[0].ToField value="1"/>
        <select name=FieldMappings[1].FromField class="form-control" asp-items="@Model.Selectlist"></select>
        <input name=FieldMappings[1].ToField value="2"/>
    </tbody>
</table>
<input class="btn btn-primary" type="submit" value="Save Changes" />

Result? enter image description here

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1