asp.net-mvclisthidden-fieldhtml.beginform

Passing a List as @Html.Hidden and the list is being deleted


I am having an issue sending through a list from a form to a controller to work with and edit. It just destroys the list and passes through just one empty string. I need the list to maintain so that i can add to it in the controller.

The view looks like this:

@using (Html.BeginForm("AddToBasket", "Home", FormMethod.Post))
        {
            @Html.Hidden("product", product.Product)
            @Html.Hidden("basketItems", Model.BasketItems)

            <h3>@product.Product (£@product.Price)</h3>
            <div class="menu-item-quantity">
                <h4>Quanitity: @Html.TextBox("quantity", 0, new { @class = "form-control-quantity" })</h4>
            </div>
            <input class="btn btn-lg" type="submit" value="Add to basket" />
        }

The controller:

public ActionResult AddToBasket(string product, int quantity, List<string>basketItems)
        {   
            var products = new GetProductsList<ProductLookup>().Query().ToList();

            var Result = new BuyViewModel
            {
                Products = products,
                BasketItems = basketItems.ToList()
            };

            return View("Buy", Result);
        }

and the model:

public class BuyViewModel
    {
        public IList<ProductLookup> Products { get; set; }

        public List<string> BasketItems { get; set; }
    }

How would i get that list through to the controller in one piece??


Solution

  • Instead of returning one item, you're looking for returning a list.

    One way to do that is returning an array of form elements with the same name, so the controller can stitch that back into a list.

    Here's an example:

    Instead of:

    @Html.Hidden("basketItems", Model.BasketItems)
    

    Add this:

    for (int i = 0; i < Model.BasketItems.Count; i++)
    {
        @Html.HiddenFor(m => m.BasketItems[i])
    }
    

    This will generate the following html:

    <input type="hidden" name="basketItems[0]" value="item 1" />
    <input type="hidden" name="basketItems[1]" value="item 2" />
    

    (maybe you don't need the @ in the @Html, but I haven't checked that)

    update I created a test project:

    TestViewModel.cs

    using System;
    using System.Collections.Generic;
    
    namespace WebApplication1.Models
    {
        public class TestViewModel
        {
            public string Name { get; set; }
            public List<String> Items { get; set; }
        }
    }
    

    HomeController.cs

    using System.Collections.Generic;
    using System.Web.Mvc;
    using WebApplication1.Models;
    
    namespace WebApplication1.Controllers
    {
        public class HomeController : Controller
        {
            public ActionResult Index()
            {
                var viewModel = new TestViewModel {
                    Name = "Test name",
                    Items = new List<string> { "item 1" , "item 2" }
                };
    
    
                return View(viewModel);
            }
    
            [HttpPost]
            public ActionResult Index(string name, List<string> items)
            {
                var viewModel = new TestViewModel
                {
                    Name = name,
                    Items = items
                };
                return View(viewModel);
            }
    
        }
    }
    

    Index.cshtml

    @model WebApplication1.Models.TestViewModel
    @{
        ViewBag.Title = "Home Page";
    }
    
    @using (Html.BeginForm("Index", "Home", FormMethod.Post))
    {
    
        for (int i = 0; i < Model.Items.Count; i++)
        {
            @Html.HiddenFor(m => m.Items[i])
        }
    
    
        <div class="">
            @Html.TextBoxFor(m => m.Name)
        </div>
    
    
        <input type="submit" value="save" />
    }
    

    When I click save, the form is displayed again but now with the values from the previous form