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();
}
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.
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.
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.
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>
}
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With