Guillaume Longtin Guillaume Longtin - 22 days ago 10
ASP.NET (C#) Question

Foreach in a Foreach in MVC View

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-one one-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-one one-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 !!!!

Answer

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.