PR U-6a: MainLayout wraps @Body in an ErrorBoundary
Add a Blazor built-in ErrorBoundary around @Body with a friendly FluentMessageBar + retry / home buttons. Also hook NavigationManager.LocationChanged so the boundary resets on any nav (otherwise the error state persists across NavigateTo and "Zur Startseite" still shows the failure banner). U-6 split into U-6a (this) and U-6b (per-handler try/catch to route expected exceptions to IMessageService — deferred). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
58ca05f83e
commit
562338d0c4
2 changed files with 42 additions and 3 deletions
|
|
@ -1,4 +1,6 @@
|
||||||
@inherits LayoutComponentBase
|
@inherits LayoutComponentBase
|
||||||
|
@inject NavigationManager Navigation
|
||||||
|
@implements IDisposable
|
||||||
|
|
||||||
<FluentLayout>
|
<FluentLayout>
|
||||||
<FluentHeader Class="hidePrint">
|
<FluentHeader Class="hidePrint">
|
||||||
|
|
@ -9,7 +11,24 @@
|
||||||
<FluentBodyContent Class="body-content">
|
<FluentBodyContent Class="body-content">
|
||||||
<FluentMessageBarProvider Section="MESSAGES_TOP" @rendermode="InteractiveServer" />
|
<FluentMessageBarProvider Section="MESSAGES_TOP" @rendermode="InteractiveServer" />
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
<ErrorBoundary @ref="errorBoundary">
|
||||||
|
<ChildContent>
|
||||||
@Body
|
@Body
|
||||||
|
</ChildContent>
|
||||||
|
<ErrorContent>
|
||||||
|
<FluentStack Orientation="Orientation.Vertical" Style="padding: 2rem; max-width: 640px;">
|
||||||
|
<FluentMessageBar Intent="MessageIntent.Error"
|
||||||
|
Title="Ein Fehler ist aufgetreten"
|
||||||
|
AllowDismiss="false">
|
||||||
|
Diese Seite konnte nicht angezeigt werden. Details stehen im Server-Log.
|
||||||
|
</FluentMessageBar>
|
||||||
|
<FluentStack Orientation="Orientation.Horizontal">
|
||||||
|
<FluentButton Appearance="Appearance.Neutral" OnClick="Recover">Neu versuchen</FluentButton>
|
||||||
|
<FluentAnchor Href="/" Appearance="Appearance.Neutral">Zur Startseite</FluentAnchor>
|
||||||
|
</FluentStack>
|
||||||
|
</FluentStack>
|
||||||
|
</ErrorContent>
|
||||||
|
</ErrorBoundary>
|
||||||
</div>
|
</div>
|
||||||
</FluentBodyContent>
|
</FluentBodyContent>
|
||||||
</FluentStack>
|
</FluentStack>
|
||||||
|
|
@ -29,3 +48,22 @@
|
||||||
<a href="." class="reload">Reload</a>
|
<a href="." class="reload">Reload</a>
|
||||||
<span class="dismiss">🗙</span>
|
<span class="dismiss">🗙</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private ErrorBoundary? errorBoundary;
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
// ErrorBoundary state persists across NavigationManager.NavigateTo, so the
|
||||||
|
// error page would linger even after the user clicked "Zur Startseite".
|
||||||
|
// Reset on any location change.
|
||||||
|
Navigation.LocationChanged += OnLocationChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnLocationChanged(object? sender, Microsoft.AspNetCore.Components.Routing.LocationChangedEventArgs e)
|
||||||
|
=> errorBoundary?.Recover();
|
||||||
|
|
||||||
|
private void Recover() => errorBoundary?.Recover();
|
||||||
|
|
||||||
|
public void Dispose() => Navigation.LocationChanged -= OnLocationChanged;
|
||||||
|
}
|
||||||
|
|
@ -165,7 +165,8 @@ Split into two PRs on delivery because the header path touches ~15 pages.
|
||||||
### Phase 3 — Loading and error UX consistency
|
### Phase 3 — Loading and error UX consistency
|
||||||
|
|
||||||
- **U-5** Shared `<LoadingWhen Data="@x">` component with a consistent skeleton visual. Adopt across every page currently using ad-hoc `@if (x is null) { <p>Lädt…</p> return; }`.
|
- **U-5** Shared `<LoadingWhen Data="@x">` component with a consistent skeleton visual. Adopt across every page currently using ad-hoc `@if (x is null) { <p>Lädt…</p> return; }`.
|
||||||
- **U-6** Wrap each page (or the layout's `@Body`) in `<ErrorBoundary>` with a friendly retry UI. Route `SaveTransactionAsync` / `DeleteAccountAsync` etc. exceptions to `IMessageService` instead of bubbling.
|
- **U-6a** Wrap `MainLayout`'s `@Body` in `<ErrorBoundary>` with a friendly retry UI. Reset on any `NavigationManager.LocationChanged` so the error state doesn't stick after the user clicks "Zur Startseite".
|
||||||
|
- **U-6b** Route expected `SaveTransactionAsync` / `DeleteAccountAsync` / etc. exceptions to `IMessageService` — try/catch in each of the ~15 event handlers with a shared error-formatting helper.
|
||||||
|
|
||||||
### Phase 4 — Feedback + polish for mutations
|
### Phase 4 — Feedback + polish for mutations
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue