Aspnetcore: Support canceling of navigation

Created on 29 Oct 2018  路  6Comments  路  Source: dotnet/aspnetcore

There is an event OnLocationChanged on IUriHelper, it would be great to have support for canceling or more particularly confirming unintentional navigation.

The use case is a simple form that users can change, but to don't lose any changes unintentionally, if the user tries to navigate away and the form has changed, we would like to ask him to confirm or cancel navigation.

area-blazor enhancement

Most helpful comment

It would be great if this feature will be included in the ASP.NET Core 3.0 Release.

All 6 comments

It would be great if this feature will be included in the ASP.NET Core 3.0 Release.

In Blazor Server, event handling is entirely async and browsers require that certain handlers, which includes navigation, finish synchronously. Unfortunately this is not a feature we could support. If this is absolutely necessary, we recommend using JS / JSInterop in your application to handle this.

Then what is your recommendation to prevent the user from navigation away when they have unsaved changes on a form? This is a super common scenario. Especially in LOB applications we use Blazor primarily for.

Maybe there could be a hidden InProcessNavigationManager (like we have IJSInProcessRuntime) for Blazor WASM?

It could support Blazor WASM navigation use-cases.

I was able to implement a partial solution, but it's still pretty messy. This is the way it works:

  • JavaScript: subscribe globally to beforeunload event (deploy custom script)
  • Blazor: subscribe to the OnFieldChange event of EditContext on OnParametersSet (also, don't forget to handle previous subscriptions as the component may be reused)
  • Blazor: in the event handler, evaluate whether the form/model has effectively changed
  • Blazor: communicate the state asynchronously via JS interop and set a flag like formHasPendingChanges = true
  • JavaScript: check flag in event handler and prompt if neccessary
  • Blazor: revert flag on OnParametersSet to false, because the component may be reused
  • Blazor: revert flag on Component Dispose to false
  • Blazor: unsubscribe from OnFieldChange on Dispose to prevent leaving unwanted references to a dead component

This is not only a lot of custom code, but also very inefficient. If I reevaluate the state of the form at every keystroke and even push the change asynchronously to the JS side, it would burn a lot of resources. So, on top of this, I had to introduce Reactive Extensions, to Throttle and DistinctUntilChanged the changes and give some relief after quick changes. But this approach introduces a delay as well, so if somebody is too fast or simply presses the wrong key accidentally, they may close the form before we would recognize it has changed.

Also, it handles only events initiated by the browser, so like: closing of tab (or window), refresh, navigating away to a different site. But it doesn't handle the majority of cases, when the route changes inside Blazor to another component without "unloading" the site.

If anybody has a better approach, it would worth sharing, at least I would be very happy to see what is the recommended way of implementing this feature properly, which is again: pretty common in any application where you input any data.

There is a new issue and even a pull request over at #23886

Was this page helpful?
0 / 5 - 0 ratings