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

2

5

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??

Degression answered 19/4, 2018 at 9:26 Comment(9)
See #12697488Guiltless
I can sort of see how that post is similar to me but it seems like in that one,they are passing through a new list that they are entering however I am trying to pass through an existing list.Degression
The idea is the same, however. Your form now has only one input but what you really would like to do is add multiple hidden inputs, one for each item in the list.Guiltless
Sorry if i sound thick but how would I do that?Degression
No problem, been there before :) let me know if the code below works for youGuiltless
I need the list to maintain so that i can add to it in the controller. No you absolutely do not (and should not). You get the collection of basket items again in the POST method from the database/repositoryGrassy
@StephenMuecke How would you suggest doing that? This application needs to be able to handle multiple users at once.Degression
Be a strange web site if it had only one user :) - You need to store a 'basket' for each each user (in a database or Session or whatever repository you want to use)Grassy
@StephenMuecke I've had a go at doing it jao's way and i've got it half working. but out of curiosity, why should I not be doing it this way?Degression
G
6

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

Guiltless answered 19/4, 2018 at 10:29 Comment(10)
using this method, I get an error when i go through the controller and get to basketItems.Add(product); because basketItems is null :/Degression
Can you see what name the hidden elements get in the generated html?Guiltless
I can't see any hidden inputs for the basketItems in the html. The other items that I pass through using Hidden are in the html thoughDegression
here's the full source: wetransfer.com/downloads/… but it's basically the default ASP.NET MVC 5 project with the above code added inGuiltless
Definitely getting somewhere! After having a play around, comparing my project to yours, I've realised that my list is passing over fine.... PROVIDING there is already something in the list. when it's empty, it falls overDegression
great to hear that :)Guiltless
Any thoughts on how i might pass through an empty list? Obviously the first time the page is loaded there will be nothing in the basket.Degression
you could initialize the list on the first time and in the AddToBasket controller you could check if basketItems is not null and has items in it (using Any()), i.e.: if(basketItems != null && basketItems.Any()) { // do something with the list }Guiltless
That sounded very logical but then i realised that doing that will mean i will never add anything to the list because it falls over on the BasketItems.Add :PDegression
SUCCESS! I've got it :)Degression
D
0

I'm annoyed at how easy this fix was. I added a check in the controller to react if the list was passed through as a null.

[HttpPost]
        public ActionResult Buy(string product, int quantity, List<string>BasketItems)
        {

            var products = new GetProductsList<ProductLookup>().Query().ToList();


            **for (int i = 0; i < quantity; i++)
            {
                if (BasketItems == null)
                {
                    BasketItems = new List<string> { product };
                }
                else
                {
                    BasketItems.Add(product);
                }
            }** 

            var Result = new BuyViewModel
            {
                Products = products,
                BasketItems = BasketItems
            };

            return View("Buy", Result);
        }

I also changed the view to use a HiddenFor rather than hidden

Degression answered 19/4, 2018 at 13:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.