Introduction
Blazor is awesome, and there are reasons why it is! Writing reusable components so you don’t have to repeat yourself. In this article, I’ll explain inheritance in Blazor components with two examples: checkboxes and notifications. First, we’ll build a reusable checkbox component, and then we’ll move on to a notification system. Along the way, I’ll validate the logic and share expert tips to make your components cleaner and more efficient.
Creating a Reusable Checkbox Component
![Checkbox and specialized checkbox]()
Image 1. CheckBox and SpecializedCheckBox
The Basic Checkbox
Let's start by creating a simple, reusable checkbox component (Checkbox.razor). This component will have a label, maintain its checked state, and expose event callbacks to notify parent components of changes.
<div style="color:red;">
<input type="checkbox" @bind="IsChecked" @onchange="OnChange" id="@Id" />
<label for="@Id">@Label</label>
</div>
@code {
[Parameter] public string Id { get; set; } = Guid.NewGuid().ToString();
[Parameter] public string Label { get; set; } = string.Empty;
[Parameter] public bool IsChecked { get; set; }
[Parameter] public EventCallback<bool> IsCheckedChanged { get; set; }
protected async Task OnChange(ChangeEventArgs e)
{
if (bool.TryParse(e.Value?.ToString(), out var newValue))
{
IsChecked = newValue;
await IsCheckedChanged.InvokeAsync(newValue);
}
}
}
Code snippet 1. Checkbox.razor
Tips
- Validation: The checkbox binding ensures that IsCheckedChanged is only invoked when the value changes, avoiding unnecessary re-renders.
- Id Generation: Assigning a default Guid.NewGuid().ToString() to Id ensures each checkbox has a unique identifier, preventing label conflicts.
- Event Handling Fix: Added @onchange="OnChange" to explicitly trigger updates, ensuring event handling works as expected.
Extending the Base Component (SpecializedCheckbox.razor
)
Now, let's create a derived component that adds additional functionality while reusing the existing logic.
- We need to use the @inherits attribute to inherit from BaseCheckBox.razor, meaning all properties, parameters, and logic from the base class are now available in SpecializedCheckBox.
- Modifies the UI, adding styling that changes its color to green and adds an additional message.
- No need to redefine properties or event handlers.
@inherits Checkbox
<div style="color:green;">
<input type="checkbox" @bind-value="IsChecked" @bind-value:event="onchange" id="@Id" />
<label for="@Id">@Label (Specialized)</label>
<br>
@if (!string.IsNullOrEmpty(AdditionalMessage))
{
<span>@AdditionalMessage</span>
}
</div>
@code {
[Parameter] public string AdditionalMessage { get; set; }
}
}
Code snippet 2. SpecializedCheckbox.razor
Now, let's see how both components can be used
<CheckBox Label="Regular Checkbox"
IsCheckedChanged="@(checkedValue => Console.WriteLine($"BaseCheckBox: {checkedValue}"))" />
<SpecializedCheckBox Label="Checkbox"
IsCheckedChanged="@(checkedValue => Console.WriteLine($"SpecializedCheckBox: {checkedValue}"))"
AdditionalMessage="This is extended parameter" />
Code snippet 3. Usage of CheckBox and SpecializedCheckBox
Here's how both checkboxes look like:
![CheckBox and SpecializedCheckBox]()
Image 2: CheckBox and SpecializedCheckBox
Creating a Reusable Notification System
Base Notification Component
Next, we create a base notification component (NotificationBase.razor) that can be extended for different message types.
<div class="notification-base @CssClass">
<div class="notification-header">
<span class="notification-title">@Title</span>
<button class="close-button" @onclick="Dismiss">X</button>
</div>
<div class="notification-body">
@Message
</div>
</div>
@code {
[Parameter] public string Title { get; set; } = "Notification";
[Parameter] public string Message { get; set; } = string.Empty;
[Parameter] public EventCallback OnDismiss { get; set; }
[Parameter] public string CssClass { get; set; } = string.Empty;
private void Dismiss()
{
OnDismiss.InvokeAsync();
}
}
Code snippet 4. NotificationBase.razor
Tips
- Properly Handling Dismiss Events: The OnDismiss callback ensures the parent component is notified when the notification is closed.
- CSS Customization: The CssClass parameter allows styling flexibility without modifying the component itself.
Specialized Notifications
Let’s extend NotificationBase to create different types of notifications.
1. Success Notification
@inherits NotificationBase;
<span class="notification-icon">✅</span>
<NotificationBase Title="@Title"
Message="@Message"
CssClass="success-notification"
OnDismiss="@OnDismiss" />
@code {
[Parameter] public string Icon { get; set; } = "✅";
}
Code snippet 4. SuccessNotification.razor
2. Error Notification
@inherits NotificationBase;
<span class="notification-icon">❌</span>
<NotificationBase Title="@Title"
Message="@Message"
CssClass="error-notification"
OnDismiss="@OnDismiss" />
@code {
[Parameter] public string Icon { get; set; } = "❌";
}
Code snippet 5. ErrorNotification.razor
3. Warning Notification
@inherits NotificationBase;
<span class="notification-icon">@Icon</span>
<NotificationBase Title="@Title"
Message="@Message"
CssClass="warning-notification"
OnDismiss="@OnDismiss" />
@code {
[Parameter] public string Icon { get; set; } = "⚠️";
}
Code snippet 6. WarningNotification.razor
4. Info Notification
@inherits NotificationBase;
<span class="notification-icon">@Icon</span>
<NotificationBase Title="@Title"
Message="@Message"
CssClass="info-notification"
OnDismiss="@OnDismiss" />
@code {
[Parameter] public string Icon { get; set; } = "ℹ️";
}
Code snippet 7. InfoNotification.razor
Using Notifications in a Page
In NotificationDemo.razor, we use our notifications dynamically:
<div class="button-container">
<button class="btn success-btn" @onclick="() => IsSuccess = true">Show Success</button>
<button class="btn error-btn" @onclick="() => IsError = true">Show Error</button>
<button class="btn normal-btn" @onclick="() => IsInfo = true">Show Normal</button>
<button class="btn warning-btn" @onclick="() => IsWarning = true">Show Warning</button>
</div>
<div class="notification-container">
@if (IsSuccess)
{
<SuccessNotification Title="Success" Message="Phew! Everything's under control!" OnDismiss="@(() => IsSuccess = false)" />
}
@if (IsWarning)
{
<WarningNotification Title="Warning" Message="Hmm... better watch out!" OnDismiss="@(() => IsWarning = false)" />
}
@if (IsInfo)
{
<InfoNotification Title="Info" Message="Just a friendly heads-up!" OnDismiss="@(() => IsInfo = false)" />
}
@if (IsError)
{
<ErrorNotification Title="Error" Message="Oh no, something went horribly wrong!" OnDismiss="@(() => IsError = false)" />
}
</div>
@code {
bool IsSuccess = false;
bool IsError = false;
bool IsWarning = false;
bool IsInfo = false;
}
Code snippet 8. NotificationDemo.razor
Let’s admire our notification model.
![The output of specialized notifications]()
The output of specialized notifications
Conclusion
In this article, we explored the power of Blazor for building reusable and extendable components. By creating a reusable checkbox component, we demonstrated how to handle events and extend functionality without repeating code. Similarly, the notification system we showcased the flexibility of Blazor's inheritance model, allowing us to create specialized notification types while keeping the core logic intact.
Alright folks, this was fun, see you in the next one!