FluentSignals.Blazor
2.1.3
See the version list below for details.
dotnet add package FluentSignals.Blazor --version 2.1.3
NuGet\Install-Package FluentSignals.Blazor -Version 2.1.3
<PackageReference Include="FluentSignals.Blazor" Version="2.1.3" />
<PackageVersion Include="FluentSignals.Blazor" Version="2.1.3" />
<PackageReference Include="FluentSignals.Blazor" />
paket add FluentSignals.Blazor --version 2.1.3
#r "nuget: FluentSignals.Blazor, 2.1.3"
#:package FluentSignals.Blazor@2.1.3
#addin nuget:?package=FluentSignals.Blazor&version=2.1.3
#tool nuget:?package=FluentSignals.Blazor&version=2.1.3
FluentSignals.Blazor
Blazor integration for FluentSignals - A powerful reactive state management library. This package provides Blazor-specific components, SignalBus for inter-component communication, and helpers to make working with signals in Blazor applications seamless and efficient.
Features
- 📡 SignalBus - Publish/Subscribe pattern for component communication
- 📬 Queue-based subscriptions - Receive messages even if published before subscription
- 🎯 Resource components - Display any async resource with loading/error states
- 🔌 SignalR integration - Real-time data with ResourceSignalRView
- 🌐 HTTP Resource components - Ready-to-use components for HTTP resources
- 🎯 SignalComponentBase - Base component class with signal integration
- ⚡ Automatic UI updates - Components automatically re-render when signals change
- 🔄 Lifecycle integration - Proper subscription cleanup on component disposal
Installation
dotnet add package FluentSignals.Blazor
Quick Start
Basic Setup
// Program.cs
builder.Services.AddFluentSignalsBlazor(options =>
{
options.WithBaseUrl("https://api.example.com")
.WithTimeout(TimeSpan.FromSeconds(30));
});
// Or with SignalBus
builder.Services.AddFluentSignalsBlazorWithSignalBus();
Components
HttpResourceView
A component for displaying HTTP resources with built-in loading, error, and success states. Supports dynamic request building with signal subscriptions for automatic data reloading when signals change.
Basic Usage
<HttpResourceView T="WeatherData" Url="/api/weather">
<Success>
<WeatherDisplay Data="@context" />
</Success>
</HttpResourceView>
<HttpResourceView T="User[]" Url="/api/users" @ref="userView">
<Loading>
<div class="skeleton-loader">Loading users...</div>
</Loading>
<ErrorContent>
<div class="error-panel">
<p>Failed to load users: @context.Message</p>
</div>
</ErrorContent>
<Success>
@foreach (var user in context)
{
<UserCard User="@user" />
}
</Success>
</HttpResourceView>
Dynamic Requests with Signal Subscriptions
@code {
private TypedSignal<string> searchTerm = new("");
private TypedSignal<int> currentPage = new(1);
private TypedSignal<string> sortBy = new("name");
}
<HttpResourceView T="PagedResult<Product>"
DynamicRequestBuilder="@BuildProductRequest"
SubscribeToSignals="@(new ISignal[] { searchTerm, currentPage, sortBy })">
<Success>
<ProductGrid Products="@context.Items" />
<Pagination TotalPages="@context.TotalPages"
CurrentPage="@currentPage.Value"
OnPageChange="@(page => currentPage.Value = page)" />
</Success>
</HttpResourceView>
@code {
private HttpRequestMessage BuildProductRequest()
{
var url = $"/api/products?search={searchTerm.Value}&page={currentPage.Value}&sort={sortBy.Value}";
return new HttpRequestMessage(HttpMethod.Get, url);
}
}
Parameters
Url
- The URL to fetch data from (simple GET requests)RequestBuilder
- Function that builds the HTTP requestDynamicRequestBuilder
- Function that builds requests using current signal valuesSubscribeToSignals
- Array of signals to subscribe to for automatic reloadingLoadOnInit
- Whether to load data on component initialization (default: true)ShowRetryButton
- Show retry button on errors (default: true)Loading
- Custom loading contentSuccess
- Content to display when data is loadedEmpty
- Content to display when no data is availableErrorContent
- Custom error contentOnDataLoaded
- Callback when data is successfully loadedOnError
- Callback when an error occursOnResourceCreated
- Callback when the resource is created
Methods
RefreshAsync()
- Manually refresh the dataGetResource()
- Get access to the underlying HttpResource
Real-World Examples
Search with Pagination
@code {
private TypedSignal<string> searchTerm = new("");
private TypedSignal<int> currentPage = new(1);
private TypedSignal<int> pageSize = new(20);
}
<input type="text" @bind="searchTerm.Value" @bind:event="oninput"
placeholder="Search..." class="form-control mb-3" />
<HttpResourceView T="PagedResult<Product>"
DynamicRequestBuilder="@(() => new HttpRequestMessage(HttpMethod.Get,
$"/api/products?q={searchTerm.Value}&page={currentPage.Value}&size={pageSize.Value}"))"
SubscribeToSignals="@(new[] { searchTerm, currentPage, pageSize })">
<Success>
@foreach (var product in context.Items)
{
<ProductCard Item="@product" />
}
<Pagination CurrentPage="@currentPage.Value"
TotalPages="@context.TotalPages"
OnPageChange="@(page => currentPage.Value = page)" />
</Success>
</HttpResourceView>
Cursor-Based Pagination (Infinite Scroll)
@code {
private TypedSignal<string?> nextCursor = new(null);
private List<Post> allPosts = new();
}
<HttpResourceView T="CursorResult<Post>"
DynamicRequestBuilder="@(() => new HttpRequestMessage(HttpMethod.Get,
$"/api/posts?cursor={nextCursor.Value ?? ""}"))"
SubscribeToSignals="@(new[] { nextCursor })"
OnDataLoaded="@(result => { allPosts.AddRange(result.Items); })">
<Success>
<div class="posts-container">
@foreach (var post in allPosts)
{
<PostItem Data="@post" />
}
@if (context.HasMore)
{
<button @onclick="() => nextCursor.Value = context.NextCursor">
Load More
</button>
}
</div>
</Success>
</HttpResourceView>
Advanced Filtering with POST Requests
@code {
private TypedSignal<string> category = new("");
private TypedSignal<decimal?> minPrice = new(null);
private TypedSignal<decimal?> maxPrice = new(null);
private TypedSignal<bool> inStock = new(false);
private HttpRequestMessage BuildFilterRequest()
{
var filters = new {
Category = category.Value,
PriceRange = new { Min = minPrice.Value, Max = maxPrice.Value },
InStockOnly = inStock.Value
};
var request = new HttpRequestMessage(HttpMethod.Post, "/api/products/search");
request.Content = JsonContent.Create(filters);
return request;
}
}
<div class="filters">
<select @bind="category.Value">
<option value="">All Categories</option>
<option value="electronics">Electronics</option>
<option value="clothing">Clothing</option>
</select>
<input type="number" @bind="minPrice.Value" placeholder="Min Price" />
<input type="number" @bind="maxPrice.Value" placeholder="Max Price" />
<label>
<input type="checkbox" @bind="inStock.Value" />
In Stock Only
</label>
</div>
<HttpResourceView T="SearchResult<Product>"
DynamicRequestBuilder="@BuildFilterRequest"
SubscribeToSignals="@(new[] { category, minPrice, maxPrice, inStock })">
<Success>
<ProductResults Results="@context" />
</Success>
</HttpResourceView>
The key advantage is that the HttpResourceView component doesn't care about how you build your UI or structure your requests. It simply:
- Watches the signals you tell it to watch
- Rebuilds the request using your custom function when any signal changes
- Automatically fetches the data
- Provides the results to your custom content
This separation allows complete flexibility in UI design while maintaining reactive data fetching.
ResourceSignalView
A generic component for displaying any async resource with automatic state management.
ResourceSignalRView
A specialized component for SignalR real-time data with connection status display.
SignalComponentBase
A base component class that provides automatic signal integration and lifecycle management.
Typed HTTP Resources with Factory
Create strongly-typed HTTP resource classes with automatic dependency injection:
// Define your resource with the HttpResource attribute
[HttpResource("/api/users")]
public class UserResource : TypedHttpResource
{
// Parameterless constructor required for factory
public UserResource() { }
public HttpResourceRequest<User> GetById(int id) =>
Get<User>($"{BaseUrl}/{id}");
public HttpResourceRequest<IEnumerable<User>> GetAll() =>
Get<IEnumerable<User>>(BaseUrl);
public HttpResourceRequest<User> Create(User user) =>
Post<User>(BaseUrl, user);
public HttpResourceRequest<User> Update(int id, User user) =>
Put<User>($"{BaseUrl}/{id}", user);
public HttpResourceRequest Delete(int id) =>
Delete($"{BaseUrl}/{id}");
}
Register and use typed resources with factory:
// Registration in Program.cs
services.AddFluentSignalsBlazor(options =>
{
options.BaseUrl = "https://api.example.com";
});
services.AddTypedHttpResourceFactory<UserResource>();
// Usage in components
@inject ITypedHttpResourceFactory<UserResource> UserFactory
@code {
private UserResource? users;
private HttpResource? userResource;
protected override async Task OnInitializedAsync()
{
// Create resource with DI-configured HttpClient
users = UserFactory.Create();
// Or create with custom options
users = UserFactory.Create(options =>
{
options.Timeout = TimeSpan.FromSeconds(60);
});
// Execute requests
userResource = await users.GetById(123).ExecuteAsync();
userResource.OnSuccess(() => ShowNotification("User loaded!"));
}
}
Alternatively, inject the resource directly:
@inject UserResource Users
@code {
protected override async Task OnInitializedAsync()
{
var resource = await Users.GetById(123).ExecuteAsync();
resource.OnSuccess(() => ShowNotification("User loaded!"));
}
}
Advanced Typed Resources
Create fully typed custom methods for complex scenarios:
[HttpResource("/api/v2")]
public class AdvancedApiResource : TypedHttpResource
{
public AdvancedApiResource() { }
// Typed search with complex criteria
public HttpResourceRequest<SearchResult<Product>> SearchProducts(ProductSearchCriteria criteria)
{
return Post<ProductSearchCriteria, SearchResult<Product>>($"{BaseUrl}/products/search", criteria)
.WithHeader("X-Search-Version", "2.0")
.ConfigureResource(r =>
{
r.OnSuccess(result => Console.WriteLine($"Found {result.Data.TotalCount} products"));
r.OnNotFound(() => Console.WriteLine("No products found"));
});
}
// Batch operations with progress tracking
public HttpResourceRequest<BatchResult> ProcessBatch(BatchRequest batch)
{
return Post<BatchRequest, BatchResult>($"{BaseUrl}/batch", batch)
.WithHeader("X-Batch-Id", Guid.NewGuid().ToString())
.ConfigureResource(r =>
{
r.IsLoading.Subscribe(loading =>
{
if (loading) ShowProgress("Processing batch...");
else HideProgress();
});
});
}
// File upload with typed metadata
public HttpResourceRequest<UploadResult> UploadFile(Stream file, FileMetadata metadata)
{
return BuildRequest<UploadResult>($"{BaseUrl}/files")
.WithMethod(HttpMethod.Post)
.WithBody(new { file, metadata })
.WithHeader("Content-Type", "multipart/form-data")
.WithQueryParam("category", metadata.Category)
.Build()
.ConfigureResource(r => r.OnServerError(() => ShowError("Upload failed")));
}
}
// Usage in component
@inject ITypedHttpResourceFactory<AdvancedApiResource> ApiFactory
@code {
private async Task SearchProducts()
{
var api = ApiFactory.Create();
var criteria = new ProductSearchCriteria
{
Query = searchText,
MinPrice = 10,
MaxPrice = 100
};
var resource = await api.SearchProducts(criteria).ExecuteAsync();
// Resource will handle success/error states automatically
}
}
SignalBus
The SignalBus provides a publish/subscribe pattern for component communication with support for both standard and queue-based subscriptions.
Documentation
For detailed documentation and examples, visit our GitHub repository.
License
This project is licensed under the MIT License.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net9.0
- FluentSignals (>= 2.1.3)
- FluentSignals.Http (>= 2.1.3)
- FluentSignals.SignalBus (>= 2.1.3)
- FluentSignals.SignalR (>= 2.1.3)
- Microsoft.AspNetCore.Components.Web (>= 9.0.6)
- Microsoft.AspNetCore.SignalR.Client (>= 9.0.6)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.6)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
v2.1.3: Fixed infinite loop issue in HttpResourceView when using OnResourceCreated callback. Improved parameter change detection to prevent unnecessary re-initialization.