简体   繁体   中英

Model List returning as null from view to controller ASP.NET Core MVC

I'm having trouble with a list within my model returning as null even tho in the view it clearly has some value.

I had trouble trying to add objects to my model's list, someone helped me and my problem was half solved. This is the code I came up with after the help

My view:

@model ActivityForm

@{
    ViewData["Title"] = "Activity Details";
}

<section class="my-sm-5">
    <div class="container">
        <div class="section-header d-flex mb-5">
            <h1 class="h-02 flex-grow-1">Activity Details</h1>
        </div>
        <div class="row">
            <div class="col-md-7">
                <div class="section-header d-flex mb-5">
                    <h1 class="h-04 flex-grow-1">Form</h1>
                </div>
                <form id="form" class="row g-3 w-90" asp-action="Create">
                    <div class="col-md-12">
                        <label asp-for="Name" class="form-label">@Html.DisplayNameFor(model => model.Name)</label>
                        <input asp-for="Name" type="text" class="form-control" id="inputEmail4"
                            placeholder="@Html.DisplayNameFor(model => model.Name)">
                        <span asp-validation-for="Name" class="invalid-feedback"></span>
                    </div>
                    <div class="col-12">
                        <label asp-for="Description" class="form-label">@Html.DisplayNameFor(model =>
                            model.Description)</label>
                        <textarea asp-for="Description" class="form-control" id="exampleFormControlTextarea1" rows="5"
                            placeholder="@Html.DisplayNameFor(model => model.Description)"></textarea>
                        <span asp-validation-for="Description" class="invalid-feedback"></span>
                    </div>
                    <div class="col-md-4">
                        <label asp-for="StartDate" class="form-label">@Html.DisplayNameFor(model =>
                            model.StartDate)</label>
                        <input asp-for="StartDate" type="date" class="form-control"
                            placeholder="@Html.DisplayNameFor(model => model.StartDate)">
                        <span asp-validation-for="StartDate" class="invalid-feedback"></span>
                    </div>
                    <div class="col-md-4">
                        <label asp-for="EndDate" class="form-label">@Html.DisplayNameFor(model => model.EndDate)</label>
                        <input asp-for="EndDate" type="date" class="form-control"
                            placeholder="@Html.DisplayNameFor(model => model.EndDate)">
                        <span asp-validation-for="EndDate" class="invalid-feedback"></span>
                    </div>
                    <div class="col-md-4 mb-6">
                        <label asp-for="Points" class="form-label">@Html.DisplayNameFor(model => model.Points)</label>
                        <input asp-for="Points" type="number" class="form-control"
                            placeholder="@Html.DisplayNameFor(model => model.Points)">
                        <span asp-validation-for="Points" class="invalid-feedback"></span>
                    </div>
                    <div class="col-8 d-grid gap-2">
                        <a class="btn btn-primary mb-2" data-bs-toggle="modal" data-bs-target="#add-award">Add award</a>
                        <div class="row">
                            <div class="col-md-6">
                                <a class="btn btn-outline-primary w-100" data-bs-toggle="modal"
                                    data-bs-target="#cancel-activity">Cancel</a>
                            </div>
                            <div class="col-md-6">
                                <a class="btn btn-outline-primary w-100" data-bs-toggle="modal"
                                    data-bs-target="#post-activity">Post Activity</a>
                            </div>
                        </div>
                    </div>

                    <div class="modal" id="add-award" tabindex="-1">
                        <div class="modal-dialog modal-dialog-centered">
                            <div class="modal-content br-20 pd-20">
                                <div class="modal-header justify-content-center">
                                    <h5 class="modal-title h-04 text-prim-color">Award details</h5>
                                </div>
                                <div class="row g-3">
                                    <div class="modal-body">
                                        <div class="row g-3">
                                            <div class="col-md-12">
                                                <label asp-for="Award.Name" class="form-label">Name</label>
                                                <input asp-for="Award.Name" type="text" class="form-control"
                                                    id="award-name">
                                            </div>
                                            <div class="col-12">
                                                <label asp-for="Award.Description" for="inputAddress"
                                                    class="form-label">Description</label>
                                                <textarea asp-for="Award.Description" class="form-control"
                                                    id="award-description" rows="5"></textarea>
                                            </div>
                                        </div>
                                    </div>
                                    <div class="modal-footer justify-content-center">
                                        <button type="button" class="btn btn-outline-primary w-100"
                                            data-bs-dismiss="modal">Close</button>
                                        <input class="btn btn-primary w-100" type="submit" value="Confirm"></input>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </form>
            </div>
            <div class="col-md-5">
                <div class="section-header d-flex mb-5">
                    <h1 class="h-04 flex-grow-1">Awards</h1>
                </div>
                <table class="table">
                    <thead>
                        <tr>
                            <th scope="col">Award name</th>
                            <th scope="col">Description</th>
                            <th scope="col"></th>
                        </tr>
                    </thead>
                    <tbody>
                        @if (Model.Awards != null)
                        {
                            foreach (var item in Model.Awards)
                            {
                                <tr>
                                    <td>@item.Name</td>
                                    <td>@item.Description</td>
                                    <td>
                                        <a class="btn btn-outline-primary btn-sm">Edit</a>
                                        <a class="btn btn-outline-primary btn-sm">Remove</a>
                                    </td>
                                </tr>
                            }
                        }
                    </tbody>
                </table>
            </div>
        </div>
    </div>
</section>

Method in controller:

        [HttpPost]
        public async Task<IActionResult> Create(ActivityForm data)
        {
            var award = data.Award;

            if (award.Name != null && award.Description != null)
            {
                if (data.Awards == null) data.Awards = new List<AwardForm>();

                data.Awards.Add(new AwardForm { Name = award.Name, Description = award.Description });

                data.Award.Name = "";
                data.Award.Description = "";

                return View(data);
            }


            if (!ModelState.IsValid)
            {
                return View(data);
            }

            string userId = User.FindFirstValue(ClaimTypes.NameIdentifier);

            await service.NewActivityAsync(data, userId);
            return RedirectToAction(nameof(Index));
        }

Model

    public class ActivityForm
    {
        public string Name { get; set; }

        public string Description { get; set; }

        public DateTime StartDate { get; set; }

        public DateTime EndDate { get; set; }

        public int Points { get; set; }

        public AwardForm Award { get; set; }

        public List<AwardForm> Awards { get; set; }
    }

Everything was now working as intended but I had one more issue left. When I try to add another Award to the list, the list is returned to the controller method as null.

I'm not really sure if the issue is related to binding, I have noticed that every other value is bound and is returning the expected value except the list which is not bound.

The list binding context should include indexes to work properly.

Try to change you foreach like below:

int i=0; @* indexing value *@
foreach (var item in Model.Awards)                        
{
    @Html.Hidden("Awards[" + i + "].Name", item.Name)
    @Html.Hidden("Awards[" + i + "].Description", item.Description)
    <tr>
        <td>@item.Name</td>
        <td>@item.Description</td>
        <td>
            <a class="btn btn-outline-primary btn-sm">Edit</a>
            <a class="btn btn-outline-primary btn-sm">Remove</a>
        </td>
    </tr>
    i++;
}

Change the foreach loop to:

<tbody>
    @if (Model.Awards != null)
    {
        @for (var i = 0; i < Model.Awards.Count; i++)
        {
            <input type="hidden" asp-for="@Model.Awards[i].Name" />
            <input type="hidden" asp-for="@Model.Awards[i].Description" />
            <tr>            
                <td>@Model.Awards[i].Name</td>
                <td>@Model.Awards[i].Description</td>
                <td>
                    <a class="btn btn-outline-primary btn-sm">Edit</a>
                    <a class="btn btn-outline-primary btn-sm">Remove</a>
                </td>
            </tr>
        }
    }
</tbody>

This has same effect to what @Victor suggested but using input tag helper instead of html helper.

Documentation for binding model to a collection: https://learn.microsoft.com/en-us/aspnet/core/mvc/models/model-binding?view=aspnetcore-6.0#collections-1

Also your form should wrap around both Form and Awards sections:

<form id="form" class="row g-3 w-90" asp-action="Create">
    <div class="col-md-7">
        <div class="section-header d-flex mb-5">
            <h1 class="h-04 flex-grow-1">Form</h1>
        </div>
        ...
    </div>
    <div class="col-md-5">
        <div class="section-header d-flex mb-5">
            <h1 class="h-04 flex-grow-1">Awards</h1>
        </div>
        ...
    </div>
</form>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM