Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

View Model IEnumerable<> property is coming back null (not binding) from post method?

I have a view model that contains a Product class type and an IEnumerable< Product > type. On the post the first level product object comes back binded from the viewmodel whereas the product enum is coming back null.

Why is the IEnumerable< Prouduct> property not getting binded to my view model per the post? Thx!

Models:

public class Product
{
    public int ID { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
}

public class ProductIndexViewModel
{
    public Product NewProduct { get; set; }
    public IEnumerable<Product> Products { get; set; }
}

public class BoringStoreContext 
{
        public BoringStoreContext()
         {
             Products = new List<Product>();
             Products.Add(new Product() { ID = 1, Name = "Sure", Price = (decimal)(1.10) });
             Products.Add(new Product() { ID = 2, Name = "Sure2", Price = (decimal)(2.10) });
         }
         public List<Product> Products {get; set;}
}

View:

@model ProductIndexViewModel

@using (@Html.BeginForm())
{
    <div>
        @Html.LabelFor(model => model.NewProduct.Name)
        @Html.EditorFor(model => model.NewProduct.Name)
    </div>
    <div>
        @Html.LabelFor(model => model.NewProduct.Price)
        @Html.EditorFor(model => model.NewProduct.Price)
    </div>
    <div>
        <input type="submit" value="Add Product" />
    </div>

        foreach (var item in Model.Products)
        { 
         <div>
        @Html.LabelFor(model => item.ID)
        @Html.EditorFor(model => item.ID)
    </div>
    <div>
        @Html.LabelFor(model => item.Name)
        @Html.EditorFor(model => item.Name)
    </div>
    <div>
        @Html.LabelFor(model => item.Price)
        @Html.EditorFor(model => item.Price)
    </div>

        }
       }

Controller:

public class HomeController : Controller
{
    BoringStoreContext db = new BoringStoreContext();

    public ActionResult Index()
    {
        ProductIndexViewModel viewModel = new ProductIndexViewModel
        {
            NewProduct = new Product(),
            Products = db.Products
        };
        return View(viewModel);
    }

    [HttpPost]
    public ActionResult Index(ProductIndexViewModel viewModel)
    {
        // ???? viewModel.Products is NULL here
        // ???? viewModel.NewProduct comes back fine

        return View();
    }
like image 826
genxgeek Avatar asked Mar 28 '12 20:03

genxgeek


People also ask

What is bind property in C#?

The Binding class is used to bind a property of a control with the property of an object. For creating a Binding object, the developer must specify the property of the control, the data source, and the table field to which the given property will be bound.

What is MVC IEnumerable?

IEnumerable will execute select query on server side, load data in-memory on client side and then filter data. IEnumerable can be used for querying data from in-memory collections like List, Array etc.

What is model binding in Web API?

The model binding system: Retrieves data from various sources such as route data, form fields, and query strings. Provides the data to controllers and Razor pages in method parameters and public properties. Converts string data to . NET types.


2 Answers

You are not using your lambda expression properly. You need to be accessing the Products list through the model. Try doing it like this:

@count = 0
foreach (var item in Model.Products)
    { 
     <div>
    @Html.LabelFor(model => model.Products[count].ID)
    @Html.EditorFor(model => model.Products[count].ID)
</div>
<div>
    @Html.LabelFor(model => model.Products[count].Name)
    @Html.EditorFor(model => model.Products[count].Name)
</div>
<div>
    @Html.LabelFor(model => model.Products[count].Price)
    @Html.EditorFor(model => model.Products[count].Price)
</div>
@count++
    }

Edit

Controller:

BoringStoreContext db = new BoringStoreContext();

    public ActionResult Index()
    {
        ProductIndexViewModel viewModel = new ProductIndexViewModel
        {
            NewProduct = new Product(),
            Products = db.Products
        };
        return View(viewModel);
    }

    [HttpPost]
    public ActionResult Index(ProductIndexViewModel viewModel)
    {
        // work with view model

        return View();
    }

Model

public class Product
{
    public int ID { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
}

public class ProductIndexViewModel
{
    public Product NewProduct { get; set; }
    public List<Product> Products { get; set; }
}

public class BoringStoreContext
{
    public BoringStoreContext()
    {
        Products = new List<Product>();
        Products.Add(new Product() { ID = 1, Name = "Sure", Price = (decimal)(1.10) });
        Products.Add(new Product() { ID = 2, Name = "Sure2", Price = (decimal)(2.10) });
    }
    public List<Product> Products { get; set; }
}

View:

@model Models.ProductIndexViewModel


@using (@Html.BeginForm())
{
<div>
    @Html.LabelFor(model => model.NewProduct.Name)
    @Html.EditorFor(model => model.NewProduct.Name)
</div>
<div>
    @Html.LabelFor(model => model.NewProduct.Price)
    @Html.EditorFor(model => model.NewProduct.Price)
</div>


for (int count = 0; count < Model.Products.Count; count++ )
{ 
    <div>
    @Html.LabelFor(model => model.Products[count].ID)
    @Html.EditorFor(model => model.Products[count].ID)
    </div>
    <div>
    @Html.LabelFor(model => model.Products[count].Name)
    @Html.EditorFor(model => model.Products[count].Name)
    </div>
    <div>
    @Html.LabelFor(model => model.Products[count].Price)
    @Html.EditorFor(model => model.Products[count].Price)
    </div>
}

<div>
    <input type="submit" value="Add Product" />
</div>
}
like image 119
Travis J Avatar answered Sep 27 '22 22:09

Travis J


Travis's answer will address the issue. Here is another approach using EditorTemplates

You can use an Editor template to display the Products and it will work fine.

1) Create a Folder called "EditorTemplates" under your Views/Home folder

2 ) Add a view called "Products" inside that. Add this code to that view

@model SO_MVC.Models.Product
@{
   Layout = null;
}
<p>
  @Html.LabelFor(x=>x.ID)
  @Html.EditorFor(x=>x.ID) 
</p>
<p>
  @Html.LabelFor(x=>x.Name)
  @Html.EditorFor(x=>x.Name)
</p>
<p>
  @Html.LabelFor(x=>x.Price)
  @Html.EditorFor(x=>x.Price)
</p> 
@Html.HiddenFor(x=>x.Id)

and in your Main View, you can call this Editor template like this

  @Html.EditorFor(m=>m.Products)
like image 24
Shyju Avatar answered Sep 27 '22 22:09

Shyju