Adding/Update Related Data ASP.NET MVC 5 - asp.net-mvc

I'm new to programming so I'm still learning.
I need to add Items to Grocery from a single view. But I can't get the data to save.
When I hit save I don't get any exceptions, the page just loads but nothing is saved to the
database. Can I get some help/guidance as to what I am doing wrong?
Data Class
public class Grocery
{
public int Id { get; set; }
public string Name { get; set; }
public virtual List<Item> Items { get; set; }
}
public class Item {
public int ItemId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
View Model
public class GroceryViewModel
{
public Grocery Grocery { get; set; }
public virtual Item Item { get; set; }
public GroceryViewModel(int GroceryId)
{
using (var db = new MyContext())
{
Grocery = db.Groceries
.Include("Items")
.SingleOrDefault(a => a.Id == GroceryId);
}
}
}
Controller
public ActionResult Edit(int GroceryId, GroceryViewModel groceryViewModel)
{
var model = new GroceryViewModel(GroceryId);
var plusItems = new Item
{
Name = groceryViewModel.Item.Name,
Description = groceryViewModel.Item.Description,
};
db.Items.Add(plusItems);
db.SaveChanges();
return RedirectToAction(model);
View
#model Project.Models.GroceryViewModel
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Groceries</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Item.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Item.Name)
#Html.ValidationMessageFor(model => model.Item.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Item.Description)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Item.Description)
#Html.ValidationMessageFor(model => model.Description)
</div>
<p>
<input type="submit" value="Add Item" />
</p>
</fieldset>

try
using (var db = new MyContext())
{
var grocery = db.Groceries.Single(a => a.Id == groceryId);
var plusItems = new Item
{
Name = groceryViewModel.Item.Name,
Description = groceryViewModel.Item.Description,
};
grocery.Items.Add(plusItems);
db.SaveChanges();
}

Related

Categories/Subcategories in asp.net mvc

We are making a marketplace like https://www.etsy.com/. And we have a problem in categorising the listings. We want to categories the item in the Listing in 3 levels, f.ex it has to be categories in this order:
Category 1
Sub Category 1.1
Sub Category 1.1.1
One of the important thing is that when you choose a category, f.ex. Electronics, then in the subcategory you can only see stuff like pc, smartphone, tv etc.
This is what we have now
public class Listing
{
public int ListingId { get; set; }
public String Name { get; set; }
public int Subcategory2Id { get; set; }
public virtual Subcategory2 Subcategory2 { get; set; }
}
public class Category
{
public int CategoryId { get; set; }
public String CategoryName { get; set; }
public virtual ICollection<Subcategory1> Subcategory1s { get; set; }
}
public class Subcategory1
{
public int Subcategory1Id { get; set; }
public String Subcategory1Name { get; set; }
public int CategoryId { get; set; }
public virtual Category Categories { get; set; }
public virtual ICollection<Subcategory2> Subcategory2s { get; set; }
}
public class Subcategory2
{
public int Subcategory2Id { get; set; }
public String Subcategory2Name { get; set; }
public int Subcategory1Id { get; set; }
public virtual Subcategory1 Subcategory1s { get; set; }
public virtual ICollection<Listing> Listings { get; set; }
}
and in the IdentityModels-ApplicationDbContext we have
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public DbSet<Listing> Listings { get; set; }
public DbSet<Category> Categories { get; set; }
public DbSet<Subcategory1> Subcategory1s { get; set; }
public DbSet<Subcategory2> Subcategory2s { get; set; }
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
The thing is we are not sure this is he right way to do it, and we dont know how to proceed, the meaning is that when you create a listing you have to have 3 drop down list, where you choose the respective categorys. So first you choose your category, and then you are able to select the subcategory 1 etc...
You should absolutely not have multiple category/subcategory entities. A category can have a parent and it can have children, but they're all "categories".
public class Category
{
public int Id { get; set; }
public int? ParentId { get; set; }
public virtual Category Parent { get; set; }
public virtual ICollection<Category> Children { get; set; }
}
ParentId is nullable, because top-level categories have no parent.
Entity Framework tends to get confused by self-referencing relationships, so you might need a little fluent config to help it out:
public class Category
{
// properties
public class Mapping : EntityTypeConfiguration<Category>
{
public class Mapping()
{
HasOptional(m => m.Parent).WithMany(m => m.Children);
}
}
}
Then, in your context:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new Category.Mapping());
}
With all that in place, when you're in your "Electronics" category, you'd show the subcategories simply by iterating over it's Children property.
UPDATE
If you need the full hierarchy rather than just one level at a time, you have a couple of options. First, you can just include multiple levels when querying:
db.Categories.Include("Children.Children");
That's not highly efficient, though, and I definitely would not recommend delving much deeper than tertiary children. However, that's all you're asking for, so this is still a workable method.
Second, you can create a stored procedure to walk the hierarchical structure for you. It's a little more complex, but with a combination of WITH and UNION ALL, you can create a flat representation of the hierarchy and then recursively use LINQ's GroupBy to work it back into a hierarchical structure.
There's a final potential third option in #Hackerman's recommendation of using HIERARCHYID, but unfortunately, to do that, you must completely remove Category from your EF context, which also means removing any direct relationships to it, as well. To relate a product to a category, you could only store the id (not as a foreign key), and then use that id to manually lookup the category in a second step. Unfortunately, while this solution makes dealing the the hierarchy easier, it makes doing everything else more difficult. Either way, it's up to you, though.
This seems to be a correct solution.
You can also use only one class (one DB table etc.) for all categories. Your "Category" class/table must then contain the reference of the parent category (nullable). That allows to make generic treatments for all categories.
For example, when the user create an item, you can display a dropdown list for the main category. If the user selects a category which contains other category, an other dropdownlist is displayed with the child categories, etc...
I giving here a example for category and subcategory with image upload.
public class ProductController : Controller
{
ApplicationDbContext db = new ApplicationDbContext();
// GET: Product
public ActionResult Index()
{
return View();
}
public ActionResult insert(int? id)
{
ViewBag.categoryList = db.Product.Where(x => x.CategoryId == 0).Select(x => new SelectListItem { Text = x.name, Value = x.Id.ToString() }).ToList();
var product = db.Product.Where(x => x.Id == id).Select(x => x).FirstOrDefault();
if (product == null) { product = new Product(); product.CategoryId = 0; }
return View(product);
}
[HttpPost]
public ActionResult insert(Product model)
{
if (Request.Files.Count > 0)
if (Request.Files["fileupload"].ContentLength > 0)
{
var fileupload = Request.Files[0];
var fileName = Path.GetFileName(fileupload.FileName);
model.Imagename = fileName;
model.ImageUrl = DateTime.Now.Ticks.ToString() + "." + fileName.Split('.')[1];
string baseurl = Server.MapPath("/") + "Images/" + model.ImageUrl;
fileupload.SaveAs(baseurl);
}
if (model.Id > 0)
{
var productEntity = db.Product.Where(x => x.Id == model.Id).Select(x => x).FirstOrDefault();
if (model.Imagename != null)
productEntity.Imagename = model.Imagename;
if (model.ImageUrl != null)
productEntity.ImageUrl = model.ImageUrl;
productEntity.name = model.name;
productEntity.CategoryId = model.CategoryId;
}
else
{
db.Product.Add(model);
}
db.SaveChanges();
return RedirectToAction("Index");
}
public ActionResult ProductList()
{
var product = db.Product.Where(x => x.Id > 0).Select(x => x).ToList();
return View(product);
}
public ActionResult getsubcategory(int id)
{
var list = db.Product.Where(x => x.CategoryId == id)
.Select(x => new SelectListItem { Text = x.name, Value = x.Id.ToString() }).ToList();
return Json(list, JsonRequestBehavior.AllowGet);
}
}
This upper controller for insert update record.
Below html code :
#model WebApplication1.Models.Product
#{
ViewBag.Title = "insert";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>insert</h2>
#using (Html.BeginForm("insert","product", FormMethod.Post,new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Product</h4>
<hr />
#Html.HiddenFor(x=>x.Id)
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
<label class="control-label col-md-2">SubCategory</label>
<div class="col-md-10">
#Html.DropDownList("SubCategory", new SelectList(ViewBag.categoryList, "Value", "Text", Model.CategoryId), "-Select-", new { #onchange = "categoryselect()", htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.CategoryId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.CategoryId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.CategoryId, new SelectList(ViewBag.categoryList, "Value", "Text", Model.CategoryId),"-Select-", new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.CategoryId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(model => model.name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Imagename, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<input id="Imagename" name="fileupload" type="file" class = "form-control" />
#*#Html.(model => model.Imagename, new { htmlAttributes = new { #class = "form-control" } })*#
#Html.ValidationMessageFor(model => model.Imagename, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
<script>
function categoryselect () {
var d = $("#SubCategory option:selected").val();
$.ajax({
url: "/product/getsubcategory?id="+d
, type: "get"
, success: function (data) {
// alert(data)
$("#CategoryId").html('<option value="">-select- </option>');
for(var i=0;i<data.length;i++)
$("#CategoryId").append('<option value="' + data[i].Value + '">' + data[i].Text + '</option>')
}
})
}
</script>
model:
namespace WebApplication1.Models
{
public class Product
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int CategoryId { get; set; }
public string name { get; set; }
public string ImageUrl { get; set; }
public string Imagename { get; set; }
}
public class Category
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int PrentId { get; set; }
public string name { get; set; }
}
}
Index Page:
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Index</h2>
<div id="productList">
</div>
<script src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
<script>
$(document).ready(function () {
$.ajax({
url:"/product/productlist"
, type: "GET"
,success:function(data)
{
$("#productList").html(data)
}
})
})
</script>
List Page:
#model IEnumerable<WebApplication1.Models.Product>
<p>
#Html.ActionLink("Create New", "Insert")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.CategoryId)
</th>
<th>
#Html.DisplayNameFor(model => model.name)
</th>
<th>
#Html.DisplayNameFor(model => model.ImageUrl)
</th>
<th>
#Html.DisplayNameFor(model => model.Imagename)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.CategoryId)
</td>
<td>
#Html.DisplayFor(modelItem => item.name)
</td>
<td>
#Html.DisplayFor(modelItem => item.ImageUrl)
</td>
<td>
#Html.DisplayFor(modelItem => item.Imagename)
</td>
<td>
#Html.ActionLink("Edit", "insert", new { id=item.Id })
</td>
</tr>
}
</table>

Partial view - Model binding in ASP.NET MVC 4

I have 2 models in my MVC 3 application, CustomerOrder and OrderDetail.
My Model OrderDetail is with List.
Model
public class CustomerOrder
{
public int CustomerId { get; set; }
public int NetPrice { get; set; }
public List<OrderDetail> Orderlist { get; set; }
public CustomerOrder()
{
Orderlist = new List<OrderDetail>();
}
}
public class OrderDetail
{
public string ProductName { get; set; }
public int Quantity { get; set; }
public int Price { get; set; }
public int TotalPrice {get{ return Price*Quantity;} }
}
This is My Controller
public ActionResult CustomerOrder()
{
return View();
}
[HttpPost]
public ActionResult CustomerOrder(CustomerOrder SelectedOrder)
{
DataBase dataBase = new DataBase();
var result = dataBase.InsertData(SelectedOrder);
ViewData["result"] = result;
return View();
}
This is My View for CustomerOrder
#model MvcCustomerOrderClass4g.Models.CustomerOrder
#{
ViewBag.Title = "CustomerOrder";
}
<h2>CustomerOrder</h2>
#using (Html.BeginForm()) {
<fieldset>
<legend>CustomerOrder</legend>
<div class="editor-label">
#Html.LabelFor(model => model.CustomerId)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.CustomerId)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.NetPrice)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.NetPrice)
</div>
<p>
<input type="submit" value="Submit" />
</p>
</fieldset>
}
#{
if (ViewData["result"] != "" && ViewData["result"] != null)
{
<script type="text/javascript" lang="javascript">
alert("Data saved Successfully");
</script>
}
}
My Model OrderDetail is with List. How to use it as a list in my view?
Here I want to add OrderDetail model. I also created another view for OrderDetails, for adding it as Partial in CustomerOrder.
The easiest way would be to create an EditorTemplate:
#model OrderDetail
#Html.HiddenFor(m => m.Id)//your model doesn't seem to have an id?
#Html.DisplayFor(m => m.ProductName)
#Html.EditorFor(m => m.ProductName)
Then in your view with the CustomerOrder model just do this:
#Html.LabelFor(model => model.OrderList)
#Html.EditorFor(model => model.OrderList)

IEnumerable Property can't return the value

I have ViewModel
public class Magazine_ViewModel
{
public int MagId { get; set; }
public string MagNo { get; set; }
public IEnumerable<Titles_ViewModel> Titles { get; set; }
}
public class Titles_ViewModel
{
public int TitleId { get; set; }
public string TitleName { get; set; }
public int pos { get; set; }
}
in Controller i do like this
var viewModel = new Magazine_ViewModel
{
Titles = numberTitles
.Select(c => new Titles_ViewModel
{
TitleId = c.TitleId,
TitleName = c.Title.TitleText,
pos=c.position
}).ToList()
};
viewModel.MagNo = magazine.MagNo.ToString();
viewModel.MagId = magazine.Id;
when i check viewModel.Titles has n records which is right.
now in the view part
<div class="editor-label">
#Html.LabelFor(model => model.MagNo)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.MagNo)
</div>
<div class="editor-field">
#Html.ListBox("wholeTitles")
<input type="button" name="add" id="add" value="add" onclick="addValue()" /><input type="button" name="remove" id="remove" value="remove" onclick="removeValue()"/>
#Html.ListBoxFor(model => model.Titles, new SelectList(Model.Titles, "TitleId", "TitleName"))
</div>
<p>
<input type="submit" value="Save" />
</p>
I add new Titles from first ListBox(wholeTitles) to second ListBox(Titles). Now the submit button is press and i want to add the new Titles to the database.
This is what i do in Post Action
[HttpPost]
public ActionResult EditTitle(Magazine_ViewModel magazineViewModel)
{
if (ModelState.IsValid)
{
var magazineTitles = new Magazine
{
Id = magazineViewModel.MagId,
NumberTitles = new List<NumberTitle>()
};
foreach (var numberTitle in magazineViewModel.Titles)
{
NumberTitle newNumberTitle = new NumberTitle();
newNumberTitle.MagazineId = magazineViewModel.MagId;
newNumberTitle.TitleId = numberTitle.TitleId;
newNumberTitle.position = 0;
unitOfWork.NumberTitleRepository.Insert(newNumberTitle);
}
}
return View();
}
but magazineViewModel.Titles shows null, I don't know what should I check to see why it is null value.
Your values not get posted back to the server. Use below in your View. This should post the titles back to the Server within the same ViewModel
for (var i = 0; i < Model.Titles.Count(); i++)
{
#Html.HiddenFor(m => m.Titles[i].TitleId)
#Html.HiddenFor(m => m.Titles[i].TitleName)
}

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();
}
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)

Composite ViewModel and UpdateModel

What is missing that restuls in unpopulated values in POST action?
Controller
public ActionResult Index()
{
var productPageViewModel = new ProductPageViewModel();
productPageViewModel.ProductPageCriteria = BuildProductPageCriteriaViewModel();
productPageViewModel.Products = GetProducts(productPageViewModel.ProductPageCriteria);
return View(productPageViewModel);
}
[HttpPost]
public ActionResult Index(ProductPageViewModel productPageViewModel, FormCollection formCollection)
{
// productPageViewModel is not populated with posted values of ProductPageCriteria.CategoryID, ProductPageCriteria.DepartmentID and ProductPageCriteria.PageSize
// formCollection has correct values
// Calling UpdateModel(productPageViewModel); has no affect - makes sense, the framework has already called it
// Calling UpdateModel(productPageViewModel.ProductPageCriteria); populates the values.
// The renderd form has names like CategoryID, DepartmentID unlike ProductPageCriteria.CategoryID, ProductPageCriteria.DepartmentID
// if the top model was passed to all partial views also.
return View(productPageViewModel);
}
Models
public class ProductPageCriteriaViewModel
{
public const int DefaultPageSize = 15;
public ProductPageCriteriaViewModel()
{
Categories = new List<Category>();
Departments = new List<Department>();
PageSize = DefaultPageSize;
}
[Display(Name = "Category")]
public int? CategoryID { get; set; }
[Display(Name = "Department")]
public int DepartmentID { get; set; }
[Display(Name = "Page Size")]
public int? PageSize { get; set; }
public List<Category> Categories { get; set; }
public List<Department> Departments { get; set; }
}
public class ProductPageViewModel
{
public ProductPageViewModel()
{
ProductPageCriteria = new ProductPageCriteriaViewModel();
Products = new List<Product>();
}
public ProductPageCriteriaViewModel ProductPageCriteria { get; set; }
public List<Product> Products { get; set; }
}
public class Product
{
public int ProductID { get; set; }
public string ProductName { get; set; }
public Category Category { get; set; }
public Department Department { get; set; }
}
public class Category
{
public int CategoryID { get; set; }
public string CategoryName { get; set; }
}
public class Department
{
public int DepartmentID { get; set; }
public string DepartmentName { get; set; }
}
View Index.cshtml
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
#Html.Partial("_ProductCriteria", Model.ProductPageCriteria)
#Html.Partial("_ProductList", Model.Products)
}
Partial View _ProductCriteria.cshtml
#model Mvc3Application4.Models.ProductPageCriteriaViewModel
<fieldset>
<legend>Criteria</legend>
<div class="editor-label">
#Html.LabelFor(model => model.CategoryID)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.CategoryID, new SelectList(Model.Categories, "CategoryID", "CategoryName", Model.CategoryID), "--- All ---")
#Html.ValidationMessageFor(model => model.CategoryID)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.DepartmentID)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.DepartmentID, new SelectList(Model.Departments, "DepartmentID", "DepartmentName", Model.DepartmentID), "--- All ---")
#Html.ValidationMessageFor(model => model.DepartmentID)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.PageSize)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.PageSize, new SelectList(new List<int> {10, 15, 20, 25, 50, 100}.Select(n => new {Value = n, Text = n}), "Value", "Text", Model.PageSize), "--- All ---")
#Html.ValidationMessageFor(model => model.PageSize)
</div>
<p>
<input type="submit" value="Search" />
</p>
</fieldset>
Partial View _ProductList.cshtml
#model IEnumerable<Mvc3Application4.Models.Product>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th></th>
<th>
ProductName
</th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.ProductID }) |
#Html.ActionLink("Details", "Details", new { id=item.ProductID }) |
#Html.ActionLink("Delete", "Delete", new { id=item.ProductID })
</td>
<td>
#item.ProductName
</td>
</tr>
}
</table>
This is off the top of my head and untested, but I believe if you pass the parent model (ProductPageViewModel) to the products criteria partial view, change the partial view to inherit this model, and change the controls to use from model => model.ProductPageCriteria.CategoryID instead of model => model.CategoryID, it should maintain the naming so that UpdateModel can match up the fields with the posted values.
Sorry for the extreme run-on sentence and if this is incorrect I'm sure I'll earn my Peer Pressure badge pretty quickly. :) Hope this helps.

Resources