4: ASP.NET Core Blazor Fundamentals
4: ASP.NET Core Blazor Fundamentals
Imagine building a custom house. Beyond the main structure (which we discussed in Module 3), you need to handle things like how people move between rooms, how specialized tools are made available, how problems are recorded, and where common items are stored. These are the "fundamentals" of making your house truly functional. Blazor fundamentals are similar – they're essential concepts for building robust and maintainable web applications.
1. ASP.NET Core Blazor Routing and Navigation
Analogy: Think of your house having a clear address system (routes) and a helpful host (navigation manager) to guide visitors between rooms.
Routing:
What it is: In Blazor, routing determines which component (think of a "page" or a "view") should be displayed based on the URL in the browser's address bar. It's how Blazor understands "if the user goes to /products, show the ProductList component."
How it works:
@pageDirective: You define a route directly in a.razorcomponent using the@pagedirective at the top.- Route Parameters: You can include parameters in your route to pass data.
App.razor: This file contains theRoutercomponent, which is responsible for finding the correct component based on the URL. It also defines what to show if a page is found (Foundtemplate) or not found (NotFoundtemplate).
@page "/counter" <h1>Counter</h1> <p role="status">Current count: @currentCount</p> <button class="btn btn-primary" @onclick="IncrementCount">Click me</button> @code { private int currentCount = 0; private void IncrementCount() { currentCount++; } }
In this example, if the browser navigates to /counter, this Counter component will be rendered.
@page "/product/{ProductId:int}" <h3>Product Details for Product ID: @ProductId</h3> @code { [Parameter] public int ProductId { get; set; } }
Navigating to /product/123 would set ProductId to 123.
<Router AppAssembly="@typeof(Program).Assembly"> <Found Context="routeData"> <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /> <FocusOnNavigate RouteData="@routeData" Selector="h1" /> </Found> <NotFound> <PageTitle>Not found</PageTitle> <LayoutView Layout="@typeof(MainLayout)"> <p role="alert">Sorry, there's nothing at this address.</p> </LayoutView> </NotFound> </Router>
Navigation:
What it is: Navigation is the programmatic way to move between pages within your Blazor application. It's like your "host" guiding visitors from the living room to the kitchen.
How it works:
NavigationManager: Blazor provides a built-in service calledNavigationManager. You inject it into your components and use its methods to navigate.NavLinkComponent: For simple navigation links in your UI (like a menu), Blazor provides theNavLinkcomponent, which automatically adds an "active" CSS class when the link matches the current URL.
@inject NavigationManager NavManager <button class="btn btn-info" @onclick="GoToHomePage">Go to Home</button> <button class="btn btn-success" @onclick="GoToProductDetails">View Product 456</button> @code { private void GoToHomePage() { NavManager.NavigateTo("/"); // Navigates to the root URL } private void GoToProductDetails() { NavManager.NavigateTo("/product/456"); // Navigates to a specific product } }
<div class="nav-item px-3"> <NavLink class="nav-link" href="counter"> <span class="oi oi-plus" aria-hidden="true"></span> Counter </NavLink> </div>
2. ASP.NET Core Blazor Dependency Injection (DI)
Analogy: Imagine you're building furniture. Instead of you (the component) having to buy all the wood, screws, and tools yourself (creating dependencies directly), you tell a "supplier" (the DI container) what you need. The supplier then provides you with the right materials (services) when you're ready to build. If the supplier changes the type of screw, you don't care, as long as you get screws.
What it is:
Dependency Injection is a design pattern used to achieve Inversion of Control (IoC). Instead of a class creating its dependencies (other objects it needs to function), the dependencies are injected into it by an external entity (the DI container).
Why it's important:
- Loose Coupling: Components don't directly depend on concrete implementations, making them easier to test and change. (If your furniture builder always asks for "a screw," it doesn't matter if the supplier provides a Philips head or a flathead, as long as it's a screw.)
- Testability: You can easily swap out real services for "mock" services during testing. (For testing, the supplier might provide fake, lightweight screws.)
- Reusability: Services can be reused across multiple components.
- Maintainability: Easier to manage complex applications as dependencies are clearly defined.
How it works:
- Registering Services (
Program.cs): You tell the DI container about the services it can provide. This typically happens inProgram.cs.- Transient: A new instance is created every time it's requested. (Like borrowing a specific tool each time you need it.)
- Scoped: A new instance is created once per client request (Blazor Server) or once per user session (Blazor WebAssembly). (Like having a personal tool set for each customer who visits your workshop.)
- Singleton: A single instance is created for the entire application lifetime. (Like a shared, central workbench available to everyone.)
builder.Services.AddTransient<IMyService, MyService>();
builder.Services.AddScoped<IProductService, ProductService>();
builder.Services.AddSingleton<ICacheService, CacheService>();
- Injecting Services (
.razorcomponents or C# classes): You request the service using the@injectdirective in a.razorcomponent or via constructor injection in a C# class.
// In MyComponent.razor @inject IProductService ProductService @code { protected override async Task OnInitializedAsync() { var products = await ProductService.GetProductsAsync(); // ... use products } }
// In a C# class (e.g., a custom service or component logic) public class MyDataService { private readonly HttpClient _httpClient; public MyDataService(HttpClient httpClient) // HttpClient is often provided by Blazor's DI { _httpClient = httpClient; } public async Task<string> GetData() { return await _httpClient.GetStringAsync("https://api.example.com/data"); } }
3. ASP.NET Core Blazor Logging
Analogy: Imagine your house has a "black box" recorder that notes down everything important that happens: "Someone opened the front door," "The light in the kitchen turned on," "There was a strange noise in the attic." This helps you understand what's going on and troubleshoot problems.
What it is:
Logging is the process of recording events and messages from your application's execution. This is crucial for debugging, monitoring, and understanding how your application behaves in different environments.
How it works:
ILogger<T>Interface: Blazor (and ASP.NET Core) uses theMicrosoft.Extensions.Logging.ILogger<T>interface. You inject an instance ofILoggerinto your components or services.- Log Levels: You can specify different severity levels for your log messages:
- Trace: Most detailed; rarely enabled in production.
- Debug: For debugging purposes.
- Information: General flow of the application.
- Warning: Unusual or unexpected events that might indicate a problem.
- Error: Errors that prevent normal operation for a specific activity.
- Critical: Catastrophic failures.
- Configuration (
appsettings.json): You can configure logging providers (where logs go, e.g., console, debug window, files, external services) and minimum log levels inappsettings.json. - Using
ILoggerin Components: - Blazor Server vs. WebAssembly Logging:
- Blazor Server: Logs typically go to the server's configured logging destinations (console, file, etc.), as the code runs on the server.
- Blazor WebAssembly: Logs by default go to the browser's developer console. You can configure custom logging providers (e.g., to send logs to a server-side API or a logging service) for production environments.
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning" // Log warnings and above for ASP.NET Core
}
}
}
@inject ILogger<Counter> Logger // Inject ILogger for this component <h3>Counter</h3> <p role="status">Current count: @currentCount</p> <button class="btn btn-primary" @onclick="IncrementCount">Click me</button> @code { private int currentCount = 0; private void IncrementCount() { currentCount++; Logger.LogInformation("Counter incremented to {Count}", currentCount); // Log an information message if (currentCount > 10) { Logger.LogWarning("Counter is getting high: {Count}", currentCount); // Log a warning } } }
4. ASP.NET Core Blazor Static Files
Analogy: These are the "fixed decorations" or "common supplies" in your house – things like wallpaper, paint, external signs, or standardized utility components that are simply delivered and used as-is, without any complex logic or interaction needed from your house's internal systems.
What it is:
Static files are assets that are directly served to the browser without any server-side processing. These include:
- HTML files
- CSS stylesheets (
.css) - JavaScript files (
.js) - Images (
.png,.jpg,.gif, etc.) - Fonts
- Favicons
How it works:
wwwrootFolder: In an ASP.NET Core Blazor project, thewwwrootfolder is designated as the web root. Any files placed directly within this folder, or in subfolders within it, are considered static files and are publicly accessible via their URL path.- If you have
wwwroot/css/site.css, it can be accessed at/css/site.css. - If you have
wwwroot/images/logo.png, it can be accessed at/images/logo.png.
- If you have
- Configuration (
Program.cs): TheUseStaticFiles()middleware inProgram.csis responsible for serving these files. This is typically configured by default when you create a Blazor project. - Referencing Static Files in Blazor Components:
- Blazor Server vs. WebAssembly: The concept is the same.
- Blazor Server: The files are served directly by the ASP.NET Core web server.
- Blazor WebAssembly: The files are downloaded to the client's browser as part of the initial application download. The
wwwrootfolder defines the root for the client-side served assets.
// Program.cs var app = builder.Build(); // ... other middleware app.UseStaticFiles(); // This middleware enables serving files from wwwroot // ... other middleware and endpoint mapping
<link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" /> <link href="css/app.css" rel="stylesheet" /> <img src="images/logo.png" alt="Company Logo" />
5. Demo Concept: Understanding Fundamentals Using a Sample App
For the demo, we'll build upon the default Blazor template (either Server or WebAssembly, but WebAssembly is great for showcasing client-side aspects). We'll modify it to highlight each of these fundamental concepts.
Sample App Idea: "My Todo List with Logging & Navigation"
This app will allow users to add, mark complete, and view a list of to-do items. We'll enhance it with our fundamental concepts.
Demo Outline:
- Start with a fresh Blazor Project:
- Create a new Blazor WebAssembly App (or Server, adjust notes as needed).
- Routing & Navigation:
- Basic Page (
Pages/Todos.razor): Add@page "/todos"directive. Add a simple heading and a list. - NavMenu Link: Modify
Shared/NavMenu.razorto include aNavLinkto/todos. - Programmatic Navigation: Add a button to the Home page (
Pages/Index.razor) that navigates to/todosusingNavigationManager. - Route Parameter: Create
Pages/TodoDetails.razorwith@page "/todo/{Id:int}". Add a button on theTodos.razorpage to navigate toTodoDetailswith a specific ID. Show how to display theIdparameter.
- Basic Page (
- Dependency Injection:
- Create a Service Interface & Implementation: Create
Data/ITodoService.cs(interface with methods likeGetTodosAsync(),AddTodoAsync(),MarkCompleteAsync()). CreateData/TodoService.cs(implementation that uses an in-memory list for simplicity). - Register the Service: In
Program.cs, addbuilder.Services.AddScoped<ITodoService, TodoService>();. - Inject and Use the Service: In
Pages/Todos.razor,@inject ITodoService TodoService. UseTodoServiceto fetch and display todos, and to add new ones.
- Create a Service Interface & Implementation: Create
- Logging:
- Inject
ILogger: InPages/Todos.razor,@inject ILogger<Todos> Logger. - Add Log Messages: When a new To-do is added, log an Information message. When a To-do is marked complete, log a Debug message. (Optional) Simulate an error (e.g., if a To-do ID is not found for details page) and log an Error message.
- Show Browser Console: Demonstrate where these logs appear in the browser's developer console (for WebAssembly). For Server, show in the IDE output window.
- Inject
- Static Files:
- Custom CSS: Create a simple
wwwroot/css/custom.cssfile (e.g., to style completed todos differently). Link this CSS file inwwwroot/index.html(for WebAssembly) orPages/_Host.cshtml(for Server). - Add an Image: Place a small image file (e.g.,
wwwroot/images/todo-icon.png). Display this image inPages/Todos.razorusing<img src="images/todo-icon.png" alt="Todo Icon" />. - Verify Access: Show how to directly access these files via their URL in the browser (e.g.,
http://localhost:5000/css/custom.css).
- Custom CSS: Create a simple
Expected Outcome of Demo:
The audience will see a functioning Blazor application where:
- They can navigate between different pages using both menu links and programmatic buttons.
- The application's data is managed by a service injected using DI, demonstrating loose coupling.
- Actions are logged, providing insight into application behavior in the browser console.
- Custom styling and images are loaded from the
wwwrootfolder as static assets.
Comments
Post a Comment