Search code examples
c#.netasp.net-mvc-5controllerhttp-post

How to send IEnumerable back from View to Controller


I'm pretty new to MVC and ASP.Net, my controller has 2 create methods for GET and POST. Through the Get method I'm generating a list and sending it into the view. In the view I'm making changes to a specific value in each var in the list and trying to send it back to the controller and the POST method.

When reaching the Post method, the value of the list is null.

The list I'm sending is a list of products and I'm using a view model to create a class with common values to pass through to the view.

To pass through the IEnumerable collection that was edited by the view I tried using BeginCollectionForm, setting the items through ViewBag, make changes to the model (@model IEnumerable<MarsBurgerV1.ViewModel.ItemVM>), but still each time the checkout button is being pressed the list in the Post method is NULL.

After a lot of tries and changes, currently my code looks as the following:

OrderController.cs (relevant parts)

public class OrderController : Controller
{
    private ApplicationDbContext db = new ApplicationDbContext();

    public ActionResult Create()
    {
        var meals = (from m in db.meals
                     select new ItemVM
                     {
                         Id = m.Id,
                         Name = m.Name,
                         Price = m.Price,
                         ItemType = "Meal",
                         ImageURL = m.ImageUrl,
                         Quantity = 0
                     }).ToList();
        var drinks = (from d in db.drinks
                      select new ItemVM
                      {
                          Id = d.Id,
                          Name = d.Name,
                          Price = d.Price,
                          ItemType = "Drink",
                          Quantity = 0
                      }).ToList();
        //More Like That....
        List<ItemVM> items = new List<ItemVM>();
        items.AddRange(meals);
        items.AddRange(drinks);//More Like...
        return View(items.ToList());
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create(IEnumerable<ItemVM> items = null)
    {
        //Here items equals to null.
        if (ModelState.IsValid) 
        {
           //.....
        }
        return View();
    }

Views/Order/Create.cshtml:

@model IEnumerable<***.ViewModel.ItemVM>

@{
    ViewBag.Title = "Create";
    var lst = Model.ToList();
    ViewBag.List = Model.ToList();
}
<style>
    tr td {
        vertical-align: middle;
    }
</style>
@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()
    <h3> Order:</h3>
    <table class="table table-condensed table-hover">
        <tr calss="table-header">
            <th>
                @Html.DisplayName("Preview")
            </th>
            <th>
                @Html.DisplayNameFor(m => m.Name)
            </th>
            <th>
                @Html.DisplayNameFor(m => m.Price)
            </th>
            <th>
                @Html.DisplayName("Quantity")
            </th>
            <th></th>
            <th></th>
        </tr>
        @for (var i = 0; i < Model.Count(); i++)
        {
            <tr>
                <td>
                    @if (Model.ElementAt(i).ImageURL != null)
                    {
                        <img src="@Url.Content(Model.ElementAt(i).ImageURL)" alt="IMAGES" height="100" width="100" />
                    }
                </td>
                <td>
                    @Html.DisplayFor(m => Model.ElementAt(i).Name)
                </td>
                <td>
                    @Html.DisplayFor(m => Model.ElementAt(i).Price)
                </td>
                <td>
                    <a type="button" class="btn btn-danger btn-xs" href="#">
                        <span class="glyphicon glyphicon-minus"></span>
                    </a>
                    @Html.EditorFor(l => lst[i].Quantity, new { htmlattribute = new { @class = "form-control" } })
                    @Html.ValidationMessageFor(model => lst[i].Quantity, "", new { @class = "text-danger" })
                    <a type="button" class="btn btn-success btn-xs" id="plus" href="#">
                        <span class="glyphicon glyphicon-plus"></span>
                    </a>
                </td>
            </tr>
        }
    </table>
<div class="form-group">
    <div class="col-md-offset-2 col-md-10">
        <input type="submit" value="Checkout" class="btn btn-primary"/>
    </div>
</div>
}

ViewModel/ItemVM.cs:

namespace ***.ViewModel
{
    public class ItemVM
    {
        [Required]
        public int Id { get; set; }
        [Required]
        public string Name { get; set; }
        [Required]
        [DataType(DataType.Currency)]
        public double Price { get; set; }
        [Required]
        public string ItemType { get; set; }
        
        public string ImageURL { get; set; }
        [Required]
        public int Quantity { get; set; }
    }
}

In the end I want to use the "Create" (HttpPost method) to create a new order based on the values in the list received from the view.

What is the proper way of doing that and receiving the IEnumerable into the POST method?


Solution

  • Ok, I was finally able to make it work. I've changed the @model to List Type, Add the (actionName, Controller, FormMethod) to the HTML Helper Html.BeginForm, Used Model[i] inside the loop to access variables and marked all unchanged variables with @Html.HiddenFor.

    Create.cshtml:

    @model List<MarsBurgerV1.ViewModel.ItemVM>
    @{
        ViewBag.Title = "Create";
    }
    @using (Html.BeginForm("Create","Order", FormMethod.Post))
    {
        @Html.AntiForgeryToken()
        <h3> Order:</h3>
        <table class="table table-condensed table-hover">
            <tr calss="table-header">
                <th>
                    @Html.DisplayName("Preview")
                </th>
                <th>
                    @Html.DisplayName("Name")
                </th>
                <th>
                    @Html.DisplayName("Price")
                </th>
                <th>
                    @Html.DisplayName("Quantity")
                </th>
                <th></th>
                <th></th>
            </tr>
            @for (var i = 0; i < Model.Count(); i++)
            {
            <tr>
                @Html.HiddenFor(m => Model[i].Id)
                @Html.HiddenFor(m => Model[i].Type)
                <td>
                    @Html.HiddenFor(m => Model[i].ImageURL)
                    @if (Model[i].ImageURL != null)
                    {
                        <img src="@Url.Content(Model[i].ImageURL)" alt="IMAGES" height="100" width="100" />
                    }
                    @Html.ValidationMessageFor(model => Model[i].ImageURL, "", new { @class = "label-control" })
                </td>
                <td>
                    @Html.HiddenFor(m => Model[i].Name)
                    @Html.DisplayFor(m => Model[i].Name)
                    @Html.ValidationMessageFor(model => Model[i].Name, "", new { @class = "label-control" })
                </td>
                <td>
                    @Html.HiddenFor(m => Model[i].Price)
                    @Html.DisplayFor(m => Model[i].Price, new { @class = "form-control" })
                    @Html.ValidationMessageFor(model => Model[i].Price, "", new { @class = "label-control" })
                </td>
                <td>
                    <a type="button" class="btn btn-danger btn-xs" href="#">
                        <span class="glyphicon glyphicon-minus"></span>
                    </a>
                    @Html.EditorFor(model => Model[i].Quantity, new { htmlattribute = new { @class = "form-control" } })
                    @Html.ValidationMessageFor(model => Model[i].Quantity, "", new { @class = "text-danger" })
                    <a type="button" class="btn btn-success btn-xs" id="plus" href="#">
                        <span class="glyphicon glyphicon-plus"></span>
                    </a>
                </td>
            </tr>
            }
        </table>
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <button type="submit" value="Checkout" class="btn btn-primary"/>
        </div>
    </div>
    }
    

    Thanks Everyone for the help.