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.