Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 496
Guillaume Longtin Avatar asked Apr 12 '13 14:04

Guillaume Longtin


People also ask

How to use foreach loop in MVC view?

To loop through Model items in ASP.NET MVC view, use foreach loop in the Controller,which returns Collection of items. Add the given below line to bind your model with . cshtml page. Now write foreach loop at view level page(.

Can we use foreach inside foreach C#?

Trying to iterate over the same collection withing it's own foreach loop will cause problems. I'm pretty sure you get a new enumerator with the 2nd foreach loop, so you shouldn't run into any issues. yes that is correct, at least for any properly written collection class...

How use ActionLink HTML in ASP NET MVC?

HTML Links The easiest way to render an HTML link in is to use the HTML. ActionLink() helper. With MVC, the Html. ActionLink() does not link to a view.


3 Answers

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.

like image 113
Johannes Setiabudi Avatar answered Sep 27 '22 18:09

Johannes Setiabudi


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)
like image 27
von v. Avatar answered Sep 27 '22 17:09

von v.


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>
    }
}  

like image 41
CR41G14 Avatar answered Sep 27 '22 17:09

CR41G14