Skip to content

Add NavigationManager.OnLocationChanging (#14962) #16607

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 37 additions & 1 deletion src/Components/Components/src/NavigationManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,25 @@ namespace Microsoft.AspNetCore.Components
/// </summary>
public abstract class NavigationManager
{
/// <summary>
/// An event that fires when the navigation location is about to change.
/// </summary>
public event EventHandler<LocationChangingEventArgs> LocationChanging
{
add
{
AssertInitialized();
_locationChanging += value;
}
remove
{
AssertInitialized();
_locationChanging -= value;
}
}

private EventHandler<LocationChangingEventArgs> _locationChanging;

/// <summary>
/// An event that fires when the navigation location has changed.
/// </summary>
Expand Down Expand Up @@ -93,7 +112,10 @@ protected set
public void NavigateTo(string uri, bool forceLoad = false)
{
AssertInitialized();
NavigateToCore(uri, forceLoad);
if (CanNavigateTo(uri, forceLoad))
{
NavigateToCore(uri, forceLoad);
}
}

/// <summary>
Expand Down Expand Up @@ -224,6 +246,20 @@ private void AssertInitialized()
}
}

private bool CanNavigateTo(string uri, bool forceLoad)
{
try
{
var args = new LocationChangingEventArgs(uri, forceLoad);
_locationChanging?.Invoke(this, args);
return !args.IsNavigationPrevented;
}
catch (Exception ex)
{
throw new LocationChangeException("An exception occurred while dispatching a location changed event.", ex);
}
}

private static bool TryGetLengthOfBaseUriPrefix(Uri baseUri, string uri, out int length)
{
if (uri.StartsWith(baseUri.OriginalString, StringComparison.Ordinal))
Expand Down
48 changes: 48 additions & 0 deletions src/Components/Components/src/Routing/LocationChangingEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;

namespace Microsoft.AspNetCore.Components.Routing
{
/// <summary>
/// <see cref="EventArgs" /> for <see cref="NavigationManager.LocationChanging" />.
/// </summary>
public class LocationChangingEventArgs : EventArgs
{
/// <summary>
/// Initializes a new instance of <see cref="LocationChangingEventArgs" />.
/// </summary>
/// <param name="location">The location of the navigation request.</param>
/// <param name="forceLoad">If true, the requested navigation will bypass client-side routing and force the browser to load the new page from the server, whether or not the URI would normally be handled by the client-side router.</param>
public LocationChangingEventArgs(string location, bool forceLoad)
{
Location = location;
ForceLoad = forceLoad;
}

/// <summary>
/// Gets the changed location.
/// </summary>
public string Location { get; }

/// <summary>
/// Gets a value that determines if navigation should be prevented.
/// </summary>
public bool IsNavigationPrevented { get; private set; }

/// <summary>
/// If true, the requested navigation will bypass client-side routing and force the browser to load the new page from the server, whether or not the URI would normally be handled by the client-side router.
/// </summary>
public bool ForceLoad { get; }

/// <summary>
/// Indicates to the <see cref="Microsoft.AspNetCore.Components.NavigationManager"/>
/// that the navigation should be prevented.
/// </summary>
public void PreventNavigation()
{
IsNavigationPrevented = true;
}
}
}
28 changes: 27 additions & 1 deletion src/Components/Components/test/NavigationManagerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,36 @@ public void ToBaseRelativePath_ThrowsForInvalidBaseRelativePaths(string baseUri,
ex.Message);
}

[Theory]
[InlineData("scheme://host/allowed", true)]
[InlineData("scheme://host/prevented", false)]
public void OnLocationChanging_CanPreventNavigation(string uri, bool allowNavigation)
{
bool hasNavigated = false;
var navigationManager = new TestNavigationManager(() => hasNavigated = true);
navigationManager.Initialize(uri, uri);
navigationManager.LocationChanging += (sender, args) =>
{
if (!allowNavigation)
args.PreventNavigation();
};
navigationManager.NavigateTo(uri);

Assert.Equal(allowNavigation, hasNavigated);
}

private class TestNavigationManager : NavigationManager
{
private Action NavigateAction;

public TestNavigationManager()
{
NavigateAction = () => throw new NotImplementedException();
}

public TestNavigationManager(Action navigateAction)
{
NavigateAction = navigateAction;
}

public TestNavigationManager(string baseUri = null, string uri = null)
Expand All @@ -110,7 +136,7 @@ public TestNavigationManager(string baseUri = null, string uri = null)

protected override void NavigateToCore(string uri, bool forceLoad)
{
throw new System.NotImplementedException();
NavigateAction();
}
}
}
Expand Down