Search code examples
.net-coretag-helpersasp.net-core-tag-helpers

Post a list to a controller with tag-helpers and dotnet core


I've seen similar questions regarding this issue, but the tag helpers is putting a spin on my question. What I want to happen is the user can choose from various seating levels, pick some seats from that level, and when they submit a bunch of objects with details regarding the number of seats picked at each level (along w/ some other details, omitted b/c I haven't got there yet) goes to the controller, which will then do some logic...

I have my model:

public class PerformanceDetailsViewModel
    {
        public int Id { get; set; }
        public string ZoneDescription { get; set; }
        public string ShowName { get; set; }
        public DateTime ShowDateTime { get; set; }
        public string VenueName { get; set; }
        public decimal Price { get; set; }
        public int NumberOfTickets { get; set; }
        public List<SelectListItem> NumberSeatSelect { get; } = new List<SelectListItem>
        {
            new SelectListItem { Value = "0", Text = "0" },
            new SelectListItem { Value = "1", Text = "1" },
            new SelectListItem { Value = "2", Text = "2" },
            new SelectListItem { Value = "3", Text = "3" },
            new SelectListItem { Value = "4", Text = "4" }
        };
    }

My view:

@model List<PerformanceDetailsViewModel>

<h1>@Model.Select(x => x.ShowName).First()</h1>
<h4>@Model.Select(x => x.ShowDateTime).First()</h4>
<h5>@Model.Select(x => x.VenueName).First()</h5>
<hr />
<div class="row">
    <div class="col-3">

    </div>
    <div class="col-3">
        Choose Number of Tickets
    </div>
</div>
<form asp-controller="PerformanceDetails" asp-action="AddToCart" method="post">

    @for (var i = 0; i < Model.Count; i++)
    {
        <div class="row">
            <div class="col-3">
                @Model[i].ZoneDescription <br />
                @Model[i].Price.ToString("C")
            </div>

            <select asp-for="@Model[i].NumberOfTickets" asp-items="Model[i].NumberSeatSelect"></select>
        </div>
        <hr />
    }
    <button type="submit" class="btn btn-sm btn-primary">Add to cart</button>
</form>

And my controller:

public void AddToCart(PerformanceDetailsViewModel numTickets)
        {
            var text = numTickets;
        }

The question is - what have I missed? The form renders fine, and has the data in it. I saw examples in various places that said to use a for loop rather than a foreach, so I'm doing that, but my model still comes across the wire empty, even though I checked in Chrome Dev tools and see that the form does have the data.

Also, I don't know whether to use a List<> for the controller method or not.

Another wrinkle is the select asp-for piece, because it expands out to a bunch of html, but I don't know how that affects the model, since each one gets a unique name that doesn't match the model name...

What am I doing wrong? And what do I need to fix?


Solution

  • Answering my own question: you can't use the asp-for="@Model[i].NumberOfTickets" because the tag-helper will convert that into [0].NumberOfTickets, which is the name that the model binder is looking for, and that will never match. So you can't use the asp-for at all, you instead have to break it down to id="@Model[i].NumberOfTickets" name="NumberOfTickets", which the model binder will like. And then your data will come through.

    Also, must update the model in order to have the list of individual name items, so convert public int NumberOfTickets { get; set; } to public List<int> NumberOfTickets { get; set; }