Food Ordering System Part 8 | How to Build a Dynamic Menu Page with Category Filtering

 

Master the art of building a dynamic Menu Page in ASP.NET Core MVC! In Part 8 of our Online Food Ordering System series, we go beyond a simple list to create a fully categorized food menu. Learn how to fetch data from SQL Server using Entity Framework Core, group items by Categories (Pizza, Burgers, Drinks), and implement a clean, responsive UI with Bootstrap 5. We cover LINQ queries for efficient data loading and show you how to build an 'Add to Cart' flow that works seamlessly. Elevate your C# Web Development skills and build a professional-grade menu today!

This MenuController is the core of your ordering system. While the HomeController only showed a few featured items, this controller handles the Full Menu and adds the ability to Filter by Category (like showing only "Pizza" or "Burgers").

Step-by-Step Explanation

  1. Dependency Injection: Just like before, we bring in ApplicationDbContext. This is your direct link to the SQL Server database.

  2. int? categoryId Parameter: The Index method now accepts an optional ID. If a user clicks "Pizza," the categoryId will be 1. If they just open the menu, it will be null (showing everything).

  3. ViewBag.Categories: We fetch all categories from the database and store them in ViewBag. This allows us to build a sidebar or navigation menu in the View so users can switch between food types.

  4. .Include(f => f.Category): This is an Eager Loading command. It tells Entity Framework to join the FoodItems table with the Categories table so we can display the category name next to the food item.

  5. The Filtering Logic:

    • f.IsAvailable: Ensures we don't show items that are out of stock.

    • (categoryId == null || f.CategoryId == categoryId): This is a clever "either/or" check. If no category is selected (null), it returns everything. If an ID is provided, it only returns items matching that specific category.

Controllers/MenuController.cs
using Microsoft.AspNetCore.Mvc;
using FoodOrderingSystem.Models;
using System.Linq;
using Microsoft.EntityFrameworkCore;

namespace FoodOrderingSystem.Controllers
{
    public class MenuController : Controller
    {
        private readonly ApplicationDbContext _context;

        public MenuController(ApplicationDbContext context)
        {
            _context = context;
        }

        public IActionResult Index(int? categoryId)
        {
            // Get categories for the filter sidebar
            var categories = _context.Categories.ToList();
            ViewBag.Categories = categories;

            // Fetch items with optional category filtering
            var foodItems = _context.FoodItems
                .Include(f => f.Category)
                .Where(f => f.IsAvailable && (categoryId == null || f.CategoryId == categoryId))
                .ToList();

            return View(foodItems);
        }
    }
}

This Index.cshtml view for the Menu is a masterpiece of functional UI. It doesn't just list food; it provides a filtered, organized browsing experience that is essential for any professional E-commerce application.

Code Explanation

  • @foreach (var cat in ViewBag.Categories): This loop creates the Category Filter Bar. It dynamically generates buttons based on the categories in your database (Pizza, Burgers, etc.). Each button links back to the same page but passes a categoryId to the controller.

  • The Grid System (col-md-3): In the Home Page, we used col-md-4 (3 items per row). Here, we use col-md-3 to show 4 items per row. This allows the user to see more of the menu at once, which is better for browsing.

  • @item.Category.Name Badge: Because we used .Include(f => f.Category) in the controller, we can now display a small badge showing exactly which category the food belongs to.

  • Hidden Input Form: Unlike a simple link, using a small form with an <input type="hidden"> is a more robust way to send the foodItemId to the AddToCart action.

  • Responsive Styling: The use of h-100 shadow on the Bootstrap cards ensures that all cards are the same height and have a professional "pop" off the page.

Views/Menu/Index.cshtml
@model List<FoodItem>
@{
    ViewData["Title"] = "Menu";
}

<div class="container my-5">
    <h2 class="text-center mb-4">Our Menu</h2>

    <!-- Category Filter Bar -->
    <div class="row mb-4">
        <div class="col-md-12 text-center">
            <a href="/Menu" class="btn btn-outline-primary me-2">All</a>
            @foreach (var cat in ViewBag.Categories)
            {
                <a href="/Menu?categoryId=@cat.Id" class="btn btn-outline-primary me-2">@cat.Name</a>
            }
        </div>
    </div>

    <!-- Food Items Grid -->
    <div class="row">
        @foreach (var item in Model)
        {
            <div class="col-md-3 mb-4">
                <div class="card food-card h-100 shadow">
                    <img src="@item.ImageUrl" class="card-img-top" alt="@item.Name" style="height: 180px; object-fit: cover;">
                    <div class="card-body">
                        <span class="badge bg-secondary">@item.Category.Name</span>
                        <h5 class="card-title mt-2">@item.Name</h5>
                        <p class="card-text text-muted small">@item.Description</p>
                        <div class="d-flex justify-content-between align-items-center mt-3">
                            <span class="h5 mb-0 text-primary">$@item.Price</span>
                            <form action="/Order/AddToCart" method="get" class="d-inline">
                                <input type="hidden" name="foodItemId" value="@item.Id" />
                                <button type="submit" class="btn btn-primary btn-sm">
                                    <i class="bi bi-cart-plus"></i> Add
                                </button>
                            </form>
                        </div>
                    </div>
                </div>
            </div>
        }
    </div>
</div>

Comments