Home > Enterprise >  ASP.NET MVC Complex Object property stays null on form submit
ASP.NET MVC Complex Object property stays null on form submit

Time:01-21

I'm getting myself acquainted with ASP.NET MVC but i'm running into something probably trivial. I have a model called ToDoList, this is a complex type with a list of ToDoItems:

public class ToDoList
{
    public Guid Id {get;set;}
    public string Name { get; set; }
    
    public virtual ICollection<ToDoItem> Items {get;set;}
}
public class ToDoItem
{
    public int Id { get; set; }
    public string Task { get; set; }
    public bool IsDone { get; set; }

    public virtual ToDoList ToDoList { get; set; }
}

My Details page with form looks like this:

@model DataLayer.TomTest.Entities.ToDoList

<h2>@Model.Name</h2>

@using (@Html.BeginForm())
{
    @Html.AntiForgeryToken()
    
    <table>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Items.First().Id)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Items.First().Task)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Items.First().IsDone)
            </th>
        </tr>
        @foreach (var toDoItem in Model.Items)
        {
            <tr>
                <td>
                    @toDoItem.Id
                </td>
                <td>
                    @Html.EditorFor(model => toDoItem.Task)
                </td>
                <td>
                    @Html.EditorFor(model => toDoItem.IsDone, new {htmlAttributes = new {@Style = "margin-left: 10px;"}})
                </td>
            </tr>
        }
    </table>
    
    <input type="submit" value="Save" />
}

And this is the method it posts to:

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Details([Bind(Include = "Id,Name,Items")] ToDoList todoList)
{
    if (ModelState.IsValid)
    {
        _context.Entry(todoList).State = EntityState.Modified;
        await _context.SaveChangesAsync();
        return View();
    }

    return View();
}

As you can see I included the [Bind] attribute as I read somewhere that would ensure i get the correct properties passed. When I debug this however, only the Id property is filled the rest remains null.

Screenshot of debugger

What can I do to fix this? Is it a mistake in the View? Or is it possible Entity Framework isn't setup correctly? Thanks in advance for your help.

CodePudding user response:

Model binding to a list doesn't work with a foreach; you need to use a for loop instead.

You'll also need hidden inputs for any properties which don't have editors within the loop.

@for (int index = 0; index < Model.Items.Count; index  )
{
    <tr>
        <td>
            @Html.HiddenFor(m => m.Items[index].Id)
            @Model.Items[index].Id
        </td>
        <td>
            @Html.EditorFor(m => m.Items[index].Task)
        </td>
        <td>
            @Html.EditorFor(m => m.Items[index].IsDone, new { htmlAttributes = new { @Style = "margin-left: 10px;" } })
        </td>
    </tr>
}

ASP.NET Wire Format for Model Binding to Arrays, Lists, Collections, Dictionaries - Scott Hanselman's Blog

  •  Tags:  
  • Related