Exercise: Split Razor Component (Partial Classes)
Exercise Set 4: Split Razor Component (Partial Classes)
Goal: Understand how to separate the C# logic of a Razor component into a separate .cs file, known as a partial class.
Scenario: Refactor the ProductDetails.razor component by moving its @code block into a partial class.
Instructions:
Part 1: Splitting ProductDetails.razor
Continue from Exercise Set 3. If you closed it, open BlazorRoutingExercise.
1. Modify ProductDetails.razor:
- Open
Pages/ProductDetails.razor. - Remove the entire
@code { ... }block. You should be left only with the HTML markup and the@page,@using, and@injectdirectives.
Pages/ProductDetails.razor (after modification):
@page "/productdetails/{ProductId:int}" @using BlazorRoutingExercise.Data @using BlazorRoutingExercise.Models @inject NavigationManager NavManager @inject IProductDataService ProductService @inject ILogger<ProductDetails> Logger <PageTitle>Product Details</PageTitle> <h1>Product Details</h1> @if (product == null) { <p><em>Product not found or loading...</em></p> } @else { <div> <h3>@product.Name</h3> <p><strong>ID:</strong> @product.Id</p> <p><strong>Price:</strong> @product.Price.ToString("C")</p> <p><strong>Description:</strong> @product.Description</p> </div> <button class="btn btn-secondary mt-3" @onclick="GoBack">Back to Products</button> }
2. Create the Partial Class:
- In the
Pagesfolder, next toProductDetails.razor, add a new class file namedProductDetails.razor.cs. (The.razor.csnaming convention is important for Visual Studio to recognize it as a partial class for the.razorfile). - Add the following code to
ProductDetails.razor.cs:
Pages/ProductDetails.razor.cs:
using Microsoft.AspNetCore.Components; // Essential for [Parameter] using Microsoft.Extensions.Logging; // Essential for ILogger using BlazorRoutingExercise.Data; // For IProductDataService using BlazorRoutingExercise.Models; // For Product model namespace BlazorRoutingExercise.Pages { // IMPORTANT: The class name must match the component name (ProductDetails) // and be declared as 'partial' to link it to ProductDetails.razor. public partial class ProductDetails : ComponentBase // Inherit from ComponentBase { // Injectables are now public properties with [Inject] attribute [Inject] public NavigationManager NavManager { get; set; } = default!; [Inject] public ILogger<ProductDetails> Logger { get; set; } = default!; [Inject] public IProductDataService ProductService { get; set; } = default!; // Route Parameter [Parameter] public int ProductId { get; set; } // Component State private Product? product; // Lifecycle methods and other logic protected override void OnParametersSet() { Logger.LogInformation("ProductDetails partial class: OnParametersSet for Product ID: {ProductId}", ProductId); product = ProductService.GetProductById(ProductId); if (product == null) { Logger.LogError("Product with ID {ProductId} not found in data service (from partial class).", ProductId); } } private void GoBack() { Logger.LogInformation("Navigating back to product list (from partial class)."); NavManager.NavigateTo("/products"); } } }
Important Notes on Partial Classes:
- The namespace (
BlazorRoutingExercise.Pages) must match the namespace whereProductDetails.razoris compiled (which isBlazorRoutingExercise.Pagesby default because it's in the Pages folder). - The class name (
ProductDetails) must exactly match the component's name. - It must be
public partial class. - It must inherit from
ComponentBase(or another component type). @injectdirectives from the.razorfile are replaced by[Inject]attributes on public properties in the partial class.[Parameter]attributes remain on public properties for route parameters.
3. Test the Split Component:
- Run the application.
- Navigate to the "Products" list and then to "Product Details".
- Verify that everything functions exactly as before. The logs you added in the previous exercise should still appear, confirming the partial class is working.
Part 2: Challenge - Splitting Another Component
Challenge:
- Repeat the process for the
ProductList.razorcomponent. - Create
ProductList.razor.cs. - Move the
@codeblock's content (state,OnInitialized,ViewProductDetailsmethod) into the partial class. - Remember to replace
@injectwith[Inject]properties. - Test thoroughly to ensure the
ProductListpage still works after the refactoring.
Solution for ProductList.razor.cs:
Pages/ProductList.razor (after modification):
@page "/products" @using BlazorRoutingExercise.Data @using BlazorRoutingExercise.Models @inject NavigationManager NavManager @inject IProductDataService ProductService @inject ILogger<ProductList> Logger <PageTitle>Products</PageTitle> <h1>Our Products</h1> <p>Explore our range of high-quality products.</p> @if (products == null) { <p><em>Loading products...</em></p> } @else { <table class="table table-striped"> <thead> <tr> <th>ID</th> <th>Name</th> <th>Price</th> <th></th> </tr> </thead> <tbody> @foreach (var product in products) { <tr> <td>@product.Id</td> <td>@product.Name</td> <td>@product.Price.ToString("C")</td> <td> <button class="btn btn-info btn-sm" @onclick="() => ViewProductDetails(product.Id)">View Details</button> </td> </tr> } </tbody> </table> }
Pages/ProductList.razor.cs (Solution):
using Microsoft.AspNetCore.Components; using Microsoft.Extensions.Logging; using BlazorRoutingExercise.Data; using BlazorRoutingExercise.Models; using System.Collections.Generic; namespace BlazorRoutingExercise.Pages { public partial class ProductList : ComponentBase { [Inject] public NavigationManager NavManager { get; set; } = default!; [Inject] public IProductDataService ProductService { get; set; } = default!; [Inject] public ILogger<ProductList> Logger { get; set; } = default!; private List<Product>? products; protected override void OnInitialized() { products = ProductService.GetAllProducts(); Logger.LogInformation("ProductList component initialized. Displaying {ProductCount} products.", products?.Count ?? 0); // Challenge: Add a LogDebug message for each product being rendered if (products != null) { foreach (var product in products) { Logger.LogDebug("Rendering product: {ProductName} (ID: {ProductId})", product.Name, product.Id); } } } private void ViewProductDetails(int productId) { Logger.LogInformation("Navigating to details for Product ID: {ProductId}", productId); NavManager.NavigateTo($"productdetails/{productId}"); } } }
This exercise demonstrates how partial classes can improve the organization and readability of your Blazor components by separating the C# logic from the HTML markup.
Comments
Post a Comment