Foreach in a Foreach in MVC View
Solution 1
Assuming your controller's action method is something like this:
public ActionResult AllCategories(int id = 0)
{
return View(db.Categories.Include(p => p.Products).ToList());
}
Modify your models to be something like this:
public class Product
{
[Key]
public int ID { get; set; }
public int CategoryID { get; set; }
//new code
public virtual Category Category { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Path { get; set; }
//remove code below
//public virtual ICollection<Category> Categories { get; set; }
}
public class Category
{
[Key]
public int CategoryID { get; set; }
public string Name { get; set; }
//new code
public virtual ICollection<Product> Products{ get; set; }
}
Then your since now the controller takes in a Category as Model (instead of a Product):
foreach (var category in Model)
{
<h3><u>@category.Name</u></h3>
<div>
<ul>
@foreach (var product in Model.Products)
{
// cut for brevity, need to add back more code from original
<li>@product.Title</li>
}
</ul>
</div>
}
UPDATED: Add ToList() to the controller return statement.
Solution 2
You have:
foreach (var category in Model.Categories)
and then
@foreach (var product in Model)
Based on that view and model it seems that Model
is of type Product
if yes then the second foreach
is not valid. Actually the first one could be the one that is invalid if you return a collection of Product
.
UPDATE:
You are right, I am returning the model of type Product. Also, I do understand what is wrong now that you've pointed it out. How am I supposed to do what I'm trying to do then if I can't do it this way?
I'm surprised your code compiles when you said you are returning a model of Product
type. Here's how you can do it:
@foreach (var category in Model)
{
<h3><u>@category.Name</u></h3>
<div>
<ul>
@foreach (var product in category.Products)
{
<li>
put the rest of your code
</li>
}
</ul>
</div>
}
That suggest that instead of returning a Product
, you return a collection of Category
with Products. Something like this in EF:
// I am typing it here directly
// so I'm not sure if this is the correct syntax.
// I assume you know how to do this,
// anyway this should give you an idea.
context.Categories.Include(o=>o.Product)
Solution 3
Try this:
It looks like you are looping for every product each time, now this is looping for each product that has the same category ID as the current category being looped
<div id="accordion1" style="text-align:justify">
@using (Html.BeginForm())
{
foreach (var category in Model.Categories)
{
<h3><u>@category.Name</u></h3>
<div>
<ul>
@foreach (var product in Model.Product.Where(m=> m.CategoryID= category.CategoryID)
{
<li>
@product.Title
@if (System.Web.Security.UrlAuthorizationModule.CheckUrlAccessForPrincipal("/admin", User, "GET"))
{
@Html.Raw(" - ")
@Html.ActionLink("Edit", "Edit", new { id = product.ID })
}
<ul>
<li>
@product.Description
</li>
</ul>
</li>
}
</ul>
</div>
}
}
Guillaume Longtin
Updated on August 03, 2022Comments
-
Guillaume Longtin over 1 year
BIG EDIT : I have edited my full post with the answer that I came up with the help of Von V and Johannes, A BIG THANK YOU GUYS !!!!
I've been trying to do a foreach loop inside a foreach loop in my index view to display my products in an accordion. Let me show you how I'm trying to do this.
Here are my models :
public class Product { [Key] public int ID { get; set; } public int CategoryID { get; set; } public string Title { get; set; } public string Description { get; set; } public string Path { get; set; } public virtual Category Category { get; set; } } public class Category { [Key] public int CategoryID { get; set; } public string Name { get; set; } public virtual ICollection<Product> Products { get; set; } }
It's a
one-oneone-many relationship, One product has only one category but a category had many products.Here is what I'm trying to do in my view :
@model IEnumerable<MyPersonalProject.Models.Product> <div id="accordion1" style="text-align:justify"> @foreach (var category in ViewBag.Categories) { <h3><u>@category.Name</u></h3> <div> @foreach (var product in Model) { if (product.CategoryID == category.CategoryID) { <table cellpadding="5" cellspacing"5" style="border:1px solid black; width:100%;background-color:White;"> <thead> <tr> <th style="background-color:black; color:white;"> @product.Title @if (System.Web.Security.UrlAuthorizationModule.CheckUrlAccessForPrincipal("/admin", User, "GET")) { @Html.Raw(" - ") @Html.ActionLink("Edit", "Edit", new { id = product.ID }, new { style = "background-color:black; color:white !important;" }) } </th> </tr> </thead> <tbody> <tr> <td style="background-color:White;"> @product.Description </td> </tr> </tbody> </table> } } </div> } </div>
I'm not quite sure this is the right way of doing it but this is pretty much what I'm trying to do. Foreach categories, put all products of that categories inside an accordion tab.
- category 1
- product 1
- product 3
- category 2
- product 2
- product 4
- category 3
- product 5
Here I will add my mapping for
my one-oneone-many (Thanks Brian P) relationship :public class MyPersonalProjectContext : DbContext { public DbSet<Product> Product { get; set; } public DbSet<Category> Category { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); modelBuilder.Entity<Product>(); modelBuilder.Entity<Category>(); } }
I will also add my controller so you can see how I did it :
public ActionResult Index() { ViewBag.Categories = db.Category.OrderBy(c => c.Name).ToList(); return View(db.Product.Include(c => c.Category).ToList()); }
BIG EDIT : I have edited my full post with the answer that I came up with the help of Von V and Johannes, A BIG THANK YOU GUYS !!!!
-
Guillaume Longtin about 11 yearsI tried going for something like this a while ago, however for me if I do Model. => all I can access is the value of my Product model. I can't go for Model.Product.
-
Guillaume Longtin about 11 yearsIt should be up right now, You are right, I am returning the model of type Product. Also, I do understand what is wrong now that you've pointed it out. How am I supposed to do what I'm trying to do then if I can't do it this way?
-
Guillaume Longtin about 11 yearsIf I go for something like this, which seems pretty good. I will have to modify my mapping. Let me update my code with my mapping so you can comment on it.
-
Guillaume Longtin about 11 yearsAlright I'm going for something like your answer. Let me get you back on this.
-
Guillaume Longtin about 11 yearsThe only thing not working out for me is my mapping, not sure it is done right. Otherwise with the way you're doing it, it will work.
-
Guillaume Longtin about 11 yearsAny thoughts on my mapping?
-
Johannes Setiabudi about 11 yearsyour mapping seems backward to me, since it's a category can have many products and each product can only have 1 category. I'd advice to correct your mapping. Why are you creating custom mapping instead of using the default EF mapping?
-
Guillaume Longtin about 11 yearsThought I had to specify it. If it can work out this way I wont modify it for my personal knowledge. But my problem ATM is when I try to call my first foreach loop it always says I can't do it since Model.Category doesn't have get enumerator.
-
Johannes Setiabudi about 11 yearsIf you are following my example - which should not require custom mapping, Category is a single object (not a collection) as a property or Product. Remember that the controller action returns a collection of Category (not Product).
-
Guillaume Longtin about 11 yearsBy doing exactly the same thing as you did, and calling @model MyPersonalProject.Category in my index view. Doing foreach(var category in Model) gives me an error saying it does not contain a public definition GetEnumerator.
-
Johannes Setiabudi about 11 yearsI have revised my code - added "ToList()" to my return statement in the controller.
-
Guillaume Longtin about 11 yearsSomehow I made it, I will update my post. Your answer and @von v answer helped very much.
-
Guillaume Longtin about 11 yearsEdited my post with my solution, what do you think?
-
Guillaume Longtin about 11 yearsThank you for your help I made it somehow. Edited my post with my solution, what do you think?