Home > Software engineering >  ASP.Net Core MVC - Complex ViewModel not binding property with list of objects
ASP.Net Core MVC - Complex ViewModel not binding property with list of objects

Time:04-30

I have a ViewModel with some properties and a list of objects. I also have a controller to create the data from the ViewModel.

Whenever the request gets back to the controller, I restrict the binding on Nickname and Items. The ModelState is invalid as the binding on the list is null.

ViewModel

public class ContainerViewModel
{
    public Guid ID { get; set; }
    public string Nickname { get; set; }
    public List<ItemViewModel> Items { get; set; }
    public Guid NewItemID { get; set; }
    public string NewItemNickname { get; set; }
    public uint NewItemQuantity { get; set; }

    public void AddNewItem()
    {
        Items.Add(new ItemViewModel {
            ID = NewItemID,
            Nickname = NewItemNickname,
            Quantity = NewItemQuantity
        });
        NewItemID = Guid.NewGuid();
        NewItemNickname = default;
        NewItemQuantity = default;
    }

    public class ItemViewModel
    {
        public Guid ID { get; set; }
        public string Nickname { get; set; }
        public uint Quantity { get; set; }
    }
}

Controller

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Nickname,Items")] ContainerViewModel containerVM, CancellationToken token)
{
    if (ModelState.IsValid) {
        await _manager.Add(PrepareDataModel(containerVM), token);
        return RedirectToAction(nameof(Index));
     }
     return View(containerVM);
}

--EDIT--

Create.cshtml

@model MyProject.Models.ContainerViewModel

@{
    ViewData["Title"] = "Create";
}

<h1>Create</h1>

<h4>Container</h4>
<hr />
<div >
    <div >
        <form asp-action="Create">
            <div asp-validation-summary="ModelOnly" ></div>
            <div >
                <label asp-for="Nickname" ></label>
                <input asp-for="Nickname"  />
                <span asp-validation-for="Nickname" ></span>
            </div>
            <div >
                <label asp-for="Quantity" ></label>
                <input asp-for="Quantity" type="number"  />
                <span asp-validation-for="Quantity" ></span>
            </div>
            <div >
                <label asp-for="Items" ></label>
                @await Html.PartialAsync("Items/_Create")
                <span asp-validation-for="Items" ></span>
            </div>
            <div >
                <input type="submit" value="Create"  />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Items/_Create.cshtml

@model MyProject.Models.ContainerViewModel

<table >
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.NewItemNickname)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.NewItemQuantity)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model.Items) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Nickname)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Quantity)
            </td>
            <td></td>
        </tr>
}
        <tr>
            <td>
                <input asp-for="NewItemNickname"  />
                <span asp-validation-for="NewItemNickname" ></span>
            </td>
            <td>
                <input asp-for="NewItemQuantity" type="number"  />
                <span asp-validation-for="NewItemQuantity" ></span>
            </td>
            <td>
                <input type="submit" asp-action="CreateNewItem" value="Add"  />
            </td>
        </tr>
    </tbody>
</table>

CodePudding user response:

In your Partial View, you just use @Html.DisplayFor(xxxxx) to show the data of Items,So these data will not be submit. Change your code like this:

Items/_Create.cshtml

//.......
@for (var i = 0; i < @Model.Items.Count; i  )
{
        <tr>
            <td>
                @Html.DisplayFor(modelItem => @Model.Items[i].Nickname)

                // use hidden to submit the data
                <input type="hidden" asp-for="@Model.Items[i].Nickname">
            </td>
            <td>
                @Html.DisplayFor(modelItem => @Model.Items[i].Quantity)
                <input type="hidden" asp-for="@Model.Items[i].Quantity">
            </td>
            <td></td>
        </tr>
 }
//.......

Result

Because you don't provide the code of CreateNewItem, I think it is may be use d to add new data in Items, I don't write this action, So ss.3 will not be submited to controller.

enter image description here

enter image description here

You can see controller can receive Items successfully.

  • Related