Section 5 of 6

View Components

🎯 What You'll Learn

  • What are view components
  • Creating view components
  • InvokeAsync method
  • Passing parameters
  • View component views
  • When to use view components

What are View Components?

View components are reusable UI components with their own logic and data access. They're like mini-controllers for specific parts of your UI.

View Components vs Partials

Feature Partial Views View Components
Logic No logic (just rendering) Has own logic/data access
Data Passed from parent view Fetches own data
Testability Limited Highly testable
Use Case Simple UI reuse Complex, self-contained widgets

Creating a View Component

1. Create the Class

ViewComponents/LowStockProductsViewComponent.cs C#
public class LowStockProductsViewComponent : ViewComponent
{
    private readonly InvenTrackDbContext _context;

    public LowStockProductsViewComponent(InvenTrackDbContext context)
    {
        _context = context;
    }

    public async Task<IViewComponentResult> InvokeAsync(int threshold = 10)
    {
        var products = await _context.Products
            .Where(p => p.Quantity < threshold)
            .ToListAsync();

        return View(products);
    }
}

2. Create the View

Views/Shared/Components/LowStockProducts/Default.cshtml HTML
@model List<Product>

<div class="alert alert-warning">
    <h4>Low Stock Alert</h4>
    @if (Model.Count == 0)
    {
        <p>All products are well-stocked!</p>
    }
    @else
    {
        <ul>
            @foreach (var product in Model)
            {
                <li>@product.Name - Only @product.Quantity left</li>
            }
        </ul>
    }
</div>
πŸ’‘ View Location

View components views are located at:
Views/Shared/Components/{ComponentName}/Default.cshtml
or
Views/{Controller}/Components/{ComponentName}/Default.cshtml

3. Invoke the View Component

Use in View HTML
<!-- Tag Helper (recommended) -->
<vc:low-stock-products threshold="5"></vc:low-stock-products>

<!-- HTML Helper -->
@await Component.InvokeAsync("LowStockProducts", new { threshold = 5 })

Passing Parameters

Multiple Parameters C#
public async Task<IViewComponentResult> InvokeAsync(int count, string category)
{
    var products = await _context.Products
        .Where(p => p.Category == category)
        .Take(count)
        .ToListAsync();

    return View(products);
}
Invoke with Parameters HTML
<vc:product-list count="10" category="Electronics"></vc:product-list>

Returning Different Views

Specify View Name C#
public async Task<IViewComponentResult> InvokeAsync(bool showDetailed)
{
    var products = await _context.Products.ToListAsync();

    if (showDetailed)
        return View("Detailed", products);
    
    return View(products); // Default.cshtml
}

View Component Results

Different Return Types C#
// Return a view
return View(model);

// Return a specific view
return View("ViewName", model);

// Return content
return Content("Hello from view component");

// Return HTML
return new HtmlContentViewComponentResult(
    new HtmlString("<h1>Hello</h1>"));

Complete InvenTrack Example

ViewComponents/RecentOrdersViewComponent.cs C#
public class RecentOrdersViewComponent : ViewComponent
{
    private readonly InvenTrackDbContext _context;

    public RecentOrdersViewComponent(InvenTrackDbContext context)
    {
        _context = context;
    }

    public async Task<IViewComponentResult> InvokeAsync(int count = 5)
    {
        var orders = await _context.Orders
            .Include(o => o.Items)
            .OrderByDescending(o => o.OrderDate)
            .Take(count)
            .ToListAsync();

        return View(orders);
    }
}
Views/Shared/Components/RecentOrders/Default.cshtml HTML
@model List<Order>

<div class="card">
    <div class="card-header">
        <h5>Recent Orders</h5>
    </div>
    <div class="card-body">
        @if (Model.Count == 0)
        {
            <p>No recent orders.</p>
        }
        @else
        {
            <table class="table table-sm">
                <thead>
                    <tr>
                        <th>Order #</th>
                        <th>Customer</th>
                        <th>Date</th>
                        <th>Items</th>
                    </tr>
                </thead>
                <tbody>
                    @foreach (var order in Model)
                    {
                        <tr>
                            <td>@order.Id</td>
                            <td>@order.CustomerName</td>
                            <td>@order.OrderDate.ToShortDateString()</td>
                            <td>@order.Items.Count</td>
                        </tr>
                    }
                </tbody>
            </table>
        }
    </div>
</div>
Views/Home/Dashboard.cshtml HTML
<h1>InvenTrack Dashboard</h1>

<div class="row">
    <div class="col-md-6">
        <vc:low-stock-products threshold="10"></vc:low-stock-products>
    </div>
    <div class="col-md-6">
        <vc:recent-orders count="10"></vc:recent-orders>
    </div>
</div>

When to Use View Components

  • Dashboard widgets: Recent orders, statistics, alerts
  • Navigation menus: Dynamic menus based on user permissions
  • Shopping cart: Display cart contents
  • Login panel: Show login status or form
  • Sidebar content: Related articles, tags, categories
  • Any reusable component that needs its own data access

Best Practices

  • Single responsibility: One component, one purpose
  • Async methods: Use async/await for database access
  • Dependency injection: Inject services in constructor
  • Naming convention: Suffix with "ViewComponent"
  • Tag helpers: Use tag helper syntax for cleaner views
  • Testability: Write unit tests for view components

Key Takeaways

  • View components: Reusable UI with logic and data access
  • ViewComponent: Base class for view components
  • InvokeAsync(): Entry point method
  • View location: Views/Shared/Components/{Name}/Default.cshtml
  • <vc:component-name>: Tag helper syntax
  • Parameters: Pass data via InvokeAsync parameters
  • Dependency injection: Inject services in constructor
  • Use cases: Widgets, menus, self-contained components
🎯 Next Steps

You now understand view components! In the final section, we'll explore Tag Helpersβ€”server-side code that helps create HTML elements in Razor files.