5

Form like this

enter image description here

ViewModel:

public class ProductViewModel
{
    public string Product { get; set; }
    public IEnumerable<SizeColorQuantityViewModel> SizeColorQuantities { get; set; }
}
public class SizeColorQuantityViewModel
{
    public string ColorId { get; set; }
    public List<SizeAndQuantity> SizeAndQuantities { get; set; }
}
public class SizeAndQuantity
{
    public int SizeId { get; set; }
    public int Quantity { get; set; }
}

View:

@model ProjectSem3.Areas.Admin.Models.ProductViewModel
@{
    ViewBag.Title = "Create";
    Layout = "~/Areas/Admin/Views/Shared/_Layout.cshtml";
    string[] ListColor = { "Red", "Blue" };
    string[] ListSize = { "S", "M", "L", "XL" };
}
    @for (var i = 0; i < ListColor.Length; i++)
    {
    <div class="form-group">
       <label class="col-md-2 control-label">Color:</label>
       <div class="col-md-2">
          @Html.TextBox("[" + i + "].ColorId", null, new { @Value = ListColor[i], @class = "form-control", @readonly = "readonly" })
       </div>
    </div>
    <div class="form-group">
       <label class="col-md-2 control-label">Size and Quantity:</label>
       @for (var j = 0; j < ListSize.Length; j++)
       {
       <div class="col-md-2">
          @Html.TextBox("[" + i + "][" + j + "].SizeAndQuantities.SizeId", null, new
          {
          @class = "form-control",
          @style = "margin-bottom: 15px",
          @Value = ListSize[j],
          @readonly = "readonly"
          })
          @Html.TextBox("[" + i + "][" + j + "].SizeAndQuantities.Quantity", null, new { @class = "form-control" })
       </div>
       }
    </div>
    }

Controller:

// GET: Admin/Product
public ActionResult Create()
{
    return View();
}
// POST: Admin/Product
[HttpPost]
public ActionResult Create(ProductViewModel product, IEnumerable
<SizeColorQuantityViewModel>
sizeColorQuantity, IEnumerable
<SizeAndQuantity>
sizeAndQuantity)
{ 
     return View();
}

I can get value which is passed from ViewModel IEnumerable<SizeColorQuantityViewModel> sizeColorQuantity to Controller. But with this model IEnumerable<SizeAndQuantity> sizeAndQuantity, I can't get any value. Cause this is 2-D Array so, I have no idea for this issues. Could you teach me how to bind value for IEnumerable<SizeAndQuantity> sizeAndQuantity.

4
  • 1
    Your generating inputs with name attributes that do not relate to your model, but not a lot of this code is making sense. Why do you have collections ListColor and ListSize in your view? Are you trying to edit the quantity for a product with Red/Small, and Red/Medium and Red/Large etc, and the same again for Blue/Small, Blue/Medium etc? Commented Aug 21, 2016 at 8:07
  • @StephenMuecke Yes, I also think a lot of ways to post a Product with Size, Color and Quantity. Have you any another ways? If you don't mind, could you explan me more? Thanks a lot :D Commented Aug 21, 2016 at 8:19
  • 1
    You need to make a lot of changes to make this work. Can you change IEnumerable<SizeColorQuantityViewModel> to List<SizeColorQuantityViewModel>? (it will make it easier) Commented Aug 21, 2016 at 8:20
  • @StephenMuecke I follow this tips: codeproject.com/Tips/855577/… Before, I also set List<SizeColorQuantityViewModel> but It seems to not be working Commented Aug 21, 2016 at 8:24

1 Answer 1

11

Your generating inputs with name attributes that have no relationship to your model, therefore cannot be bound by the DefaultModelBinder. You need to start by generating the data in the controller (not in the view) so that you can bind to your model.

The following assumes you change your SizeColorQuantities property to List<SizeColorQuantityViewModel>

public ActionResult Create()
{
    var colors = new List<string>(){ "Red", "Blue" };
    var sizes = new List<string>(){ "S", "M", "L", "XL" };

    var model = new ProductViewModel()
    {
        Product = "My product",
        SizeColorQuantities = new List<SizeColorQuantityViewModel>
    };
    foreach(var color in colors)
    {
        var child = new SizeColorQuantityViewModel()
        {
            ColorId = color,
            SizeAndQuantities = new List<SizeAndQuantity>
        };
        model.SizeColorQuantities.Add(child);
        foreach(var size in sizes)
        {
            child.SizeAndQuantities.Add(new SizeAndQuantity()
            {
                SizeId = size // assumes SizeId is changed to string, not int
            });
        }
    }
    return View(model);
}

You now have a correctly populated view model that will be passed to the view which you can bind to with strongly typed HtmlHelpers inside nested for loops

@model ProductViewModel
....
@using (Html.BeginForm())
{
    ....
    @for(int i = 0; i < Model.SizeColorQuantities.Count; i++)
    {
        @Html.TextBoxFor(m => m.SizeColorQuantities[i].ColorId, new { @class = "form-control", @readonly = "readonly" })
        for (int j = 0; j < Model.SizeColorQuantities[i].SizeAndQuantities .Count; j++)
        {
            @Html.TextBoxFor(m => m.SizeColorQuantities[i].SizeAndQuantities[j].SizeId, new { @class = "form-control", @readonly = "readonly" })
            @Html.TextBoxFor(m => m.SizeColorQuantities[i].SizeAndQuantities[j].Quantity, new { @class = "form-control" })
        }
    }
    <input type="submit" ... />
}

Note: Always use the strongly typed ***For() HtmlHelper methods and never attempt to set the value (or name) attribute when using a HtmlHelper method.

Your POST method now needs to be

[HttpPost]
public ActionResult Create(ProductViewModel model)
{
    ....
}

Note you can also use custom EditorTemplate's for your types as explained in HTML Table to ADO.NET DataTable, which also explains how your name attributes must be generated in order to bind to collections. In your case, for example your Quantity inputs need to be (compare that to what your currently generating)

<input name="SizeColorQuantities[0].SizeAndQuantities[0].Quantity" ... />
<input name="SizeColorQuantities[0].SizeAndQuantities[1].Quantity" ... />
....
<input name="SizeColorQuantities[1].SizeAndQuantities[0].Quantity" ... />
<input name="SizeColorQuantities[1].SizeAndQuantities[1].Quantity" ... />
....
Sign up to request clarification or add additional context in comments.

2 Comments

Could I ask you one more question? I release that model return from HttpPost is null. Do you have some idea? [HttpPost] public ActionResult Create(ProductViewModel model) { var test = product.Product; return View(); }
Sorry @Stephen Muecke. I find out how to resolve this problem. Cause I set params variable is "product", it's same name with properties name. Thanks you a lot. stackoverflow.com/a/35889181/4642316

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.