[Blazor] Add support for confirming navigations
See original GitHub issueSummary
Support for Location change event is a very popular ask from our users. Unfortunately, given the nature of the event adding support for it that uses browser intrinsic is going to be difficult / impossible. https://github.com/dotnet/aspnetcore/pull/24417/ attempted to solve this by hijacking browser navigation and managing it via Blazor. This is a fairly involved solution, with possible significant perf overhead, and we’d like to determine if using a narrower solution would suffice here.
A popular theme in the issue focused on preventing navigation to avoid losing unsaved changes in a component. A well-upvoted comment provides a sample for just this - https://github.com/ShaunCurtis/Blazr.Demo.EditForm/tree/master/Blazr.NavigationLocker. This spec is a way to determine if this is something we could get away with.
Motivation and goals
- Most popular issue for Blazor at the time of writing.
In-scope
- Add an API that allows components to prevent navigation in a platform-independent way.
- Provide turnkey solution that integrates with EditForm
Out-of-scope
- Not a general purpose location changing event handler.
Proposed solution
We introduce an API on NavigationManager
that allows users to confirm when a navigation is to occur.AddNavigationConfirmationAsync
returns an IAsyncDisposable
which prompts for a confirmation until the returned value is disposed.
public class NavigationManager
{
+ public ValueTask<IAsyncDisposable> AddNavigationConfirmationAsync(string message, CancellationToken cancellationToken);
}
Here’s an example of it in use:
@inject NavigationManager Nav
<form @oninput="OnInput" @onvalidsubmit="OnSubmit">
...
<button type="submit">Submit</button>
</form>
@code {
IAsyncDisposable? _navigationConfirmation;
async Task OnInput()
{
_navigationConfirmation ??= await Nav.AddNavigationConfirmationAsync("Are you sure you want to exit without saving?");
}
async Task OnSubmit() => await RemoveNavigationConfirmationAsync();
ValueTask IAsyncDisposable.DisposeAsync() => RemoveNavigationConfirmationAsync();
ValueTask ClearConfirmationAsync() => await (_navigationConfirmation?.DisposeAsync() ?? default);
}
The implementation relies on window.confirm for a navigation that is handled via Blazor’s routing and beforeunload event for all other navigation. The message
parameter is used as part of the confirm
dialog, but is likely to be ignored by the unload event since browsers do not consistently support specifying it.
In addition to this, we introduce a LockNavigationWhenDirty
component that uses EditContext
’s dirty state (IsModified() /
MarkAsUnmodified()`) to add / remove navigation confirmations. Here is what this component looks like:
public class LockNavigationWhenDirty : IAsyncDisposable
{
public LockNavigationWhenDirty(NavigationManager navigationManager);
[EditorRequired, Parameter] public string Message { get; set; }
}
Usage:
<EditForm ... @onvalidsubmit="OnValidSubmit">
<LockNavigationWhenDirty />
...
</EditForm>
@code {
async OnValidSubmit()
{
// Save content
repository.SaveAsync(..);
// Release the lock now that we're satisfied
editContext.MarkAsUnmodified();
}
}
Issue Analytics
- State:
- Created 2 years ago
- Reactions:12
- Comments:20 (5 by maintainers)
Top GitHub Comments
Can the use of
window.confirm
be replaced with a generic callback that can be handled in user code? The browser dialogs are pretty ugly and do not fit with custom site theming.Has some plan of action been decided on this? This is an essential feature that is sorely lacking.