Navigating your app via code

Access to browser navigation from Blazor is provided via the NavigationManage service. This can be injected into a Blazor component using @inject in a razor file, or the [Inject] attribute in a CS file.

The NavigationManager service has two members that are of particular interest; NavigateTo and LocationChanged.

The LocationChanged event will be explained in more detail in Detecting navigation events.

The NavigateTo method

The NavigationManager.NavigateTo method enables C# code to control the browser’s URL. As with intercepted navigations, the browser does not actually navigate to a new URL. Instead the URL in the browser is replaced, and the previous URL is inserted into the browser’s navigation history, but no request is made to the server for the content of the new page. Navigations made via NavigateTo will cause the LocationChanged event to trigger, passing the new URL and false for IsNavigationIntercepted.

For this example we’ll alter the standard Blazor template again. We’ll use what we learned previously in Route parameters and Optional route parameters.

First, delete the Index.razor and FetchData.razor pages, and remove the links to those pages inside the NavMenu.razor file. Also in NavMenu, change the href of the link to counter to href="", because we are going to make it the default page.

Edit Counter.razor and give it two routes, "/" and "/counter/{CurrentCount:int}", also make sure it descends from the CounterBase class so we can see navigation logs in the browser’s console window – the CounterBase.cs file was outlined previously in the OnLocationChanged section.

@page "/"
@page "/counter/{CurrentCount:int}"
@inherits CounterBase

We’ll also need to change the currentCount field so it is a property with a getter and setter, and decorate it as a [Parameter]. Note that it has been renamed from camelCase to PascalCase too.

[Parameter]
public int CurrentCount { get; set; }

We now have a counter page that can be accessed either simply be reaching the main page of the app, or by specifying /counter/X, where X is an integer value.

The NavigationManager was injected into our CounterBase class, and so is accessible in our Counter.razor file.

@code {
	[Parameter]
	public int CurrentCount { get; set; }

	bool forceLoad;

	void AlterBy(int adjustment)
	{
		int newCount = CurrentCount + adjustment;
		UriHelper.NavigateTo("/counter/" + newCount, forceLoad);
	}
}

We’ll call the AlterBy method from two buttons, one to increment the CurrentCount and one to decrement it. There is also an option the user will be able to select, forceLoad, which will set the relevant parameter in the call to NavigateTo so we can see the difference. The whole file should eventually look like this:

@page "/"
@page "/counter/{CurrentCount:int}"
@implements IDisposable
@inject NavigationManager NavigationManager

<h1>Counter value = @CurrentCount</h1>

<div class="form-check">
	<input @[email protected] type="checkbox" class="form-check-input" id="ForceLoadCheckbox" />
	<label class="form-check-label" for="ForceLoadCheckbox">Force page reload on navigate</label>
</div>

<div class="btn-group" role="group">
	<button @[email protected]( () => AlterBy(-1) ) class="btn btn-primary">-</button>
	<input [email protected] readonly class="form-control" />
	<button @[email protected]( () => AlterBy(1) ) class="btn btn-primary">+</button>
</div>
<a class="btn btn-secondary" href="/Counter/0">Reset</a>
<p>
	<em>Page redirects to ibm.com when count hits 10!</em>
</p>

@code {
	[Parameter]
	public int CurrentCount { get; set; }

	bool forceLoad;

	void AlterBy(int adjustment)
	{
		int newCount = CurrentCount + adjustment;

		if (newCount >= 10)
			NavigationManager.NavigateTo("https://ibm.com");

		NavigationManager.NavigateTo("/counter/" + newCount, forceLoad);
	}

	protected override void OnInitialized()
	{
		// Subscribe to the event
		NavigationManager.LocationChanged += LocationChanged;
		base.OnInitialized();
	}

	private void LocationChanged(object sender, LocationChangedEventArgs e)
	{
		string navigationMethod = e.IsNavigationIntercepted ? "HTML" : "code";
		System.Diagnostics.Debug.WriteLine($"Notified of navigation via {navigationMethod} to {e.Location}");
	}

	void IDisposable.Dispose()
	{
		// Unsubscribe from the event when our component is disposed
		NavigationManager.LocationChanged -= LocationChanged;
	}
}

Clicking the - or + buttons will call the AlterBy method which will instruct the NavigationManager service to navigate to /counter/X, where X is the value of the adjusted CurrentCount – resulting in the following output in the browser’s console:

WASM: Notified of navigation via code to http://localhost:6812/counter/1
WASM: Notified of navigation via code to http://localhost:6812/counter/2
WASM: Notified of navigation via code to http://localhost:6812/counter/3
WASM: Notified of navigation via code to http://localhost:6812/counter/4

Clicking the Reset link will result in an Intercepted navigation (i.e. not initiated in C# code) and navigate to /counter/0, resetting the value of CurrentCount.

WASM: Notified of navigation via code to http://localhost:6812/counter/1
WASM: Notified of navigation via code to http://localhost:6812/counter/2
WASM: Notified of navigation via code to http://localhost:6812/counter/3
WASM: Notified of navigation via code to http://localhost:6812/counter/4
WASM: Notified of navigation via HTML to http://localhost:6812/Counter/0

ForceLoad

The forceLoad parameter instructs Blazor to bypass its own routing system and instead have the browser actually navigate to the new URL. This will result in an HTTP request to the server to retrieve the content to display.

Note that a force load is not required in order to navigate to an off-site URL. Calling NavigateTo to another domain will invoke a full browser navigation.

Play with the GitHub example for this section. Look in the browser’s Console window to see how IsNavigationIntercepted differs when navigating via the buttons and the Reset link, and look in the browser’s Network window to see how it behaves differently based on whether you are:

  • Navigating with forceLoad set to false.
  • Navigating with forceLoad set to true.
  • Navigating to an off-site URL.

To observe the last scenario, you may wish to update your AdjustBy method to navigate off-site when CurrentValue passes a specific value.

void AlterBy(int adjustment)
{
	int newCount = CurrentCount + adjustment;

	if (newCount >= 10)
		NavigationManager.NavigateTo("https://ibm.com");

	NavigationManager.NavigateTo("/counter/" + newCount, forceLoad);
}