SwapHandler

Decouple your UI logic with distinct Event Handlers.

The Philosophy

As applications grow, Controllers tend to accumulate "UI Spaghetti Code".

  • "When a user saves a task..."
    • Update the list
    • Update the sidebar count
    • Update the project progress bar
    • Show a toast

SwapHandler lets you break this logic into small, independent classes.


How it Works

1. The Interface

Implement ISwapEventHandler<TEvent>. The generic type TEvent is a simple POCO class representing the event.

public record TaskCompletedEvent(int TaskId);

[SwapHandler]
public class TaskListHandler : ISwapEventHandler<TaskCompletedEvent>
{
    public Task HandleAsync(TaskCompletedEvent evt, SwapResponseBuilder builder, CancellationToken ct)
    {
        // Logic specific to the Task List
        builder.AlsoUpdate(SwapElements.TaskRow(evt.TaskId), "", null, SwapMode.Delete);
        return Task.CompletedTask;
    }
}

2. Auto-Discovery

The [SwapHandler] attribute ensures your handler is automatically discovered and registered by AddSwapHtmx(). No manual DI registration is needed.

3. Execution Order

Handlers run in parallel* by default. The SwapResponseBuilder is thread-safe, aggregating all AlsoUpdate calls into a single JSON/HTML response.

(Technically Task.WhenAll, they run concurrently on the server).


Advanced Usage

Priority

You can control the order if needed (though you rarely should need to).

[SwapHandler(Priority = 100)] // Runs earlier (Higher = Earlier)
public class CriticalHandler ...

Shared Services

Since Handlers are resolved from DI, you can inject your Repositories, DbContext, or Services just like in a Controller.

public class StatsHandler : ISwapEventHandler<TaskCompletedEvent>
{
    private readonly IStatsService _stats;
    
    public StatsHandler(IStatsService stats) => _stats = stats;
    
    // ...
}