Blazor Navigation
Routing
- Routing is done via the page directive (with MVC routing constraints)
- Routes.razor has a Router (scans assemblies for page directives) and RouteView.
NavigationManager
You can navigate with normal HTML anchors, NavLink (an anchor which matches the current url and sets the class active) and hook up to Navigation Manager
<ul>
<li><a href="/counter">Go to counter page</a></li>
<li><NavLink href="/counter">Go to counter page</NavLink></li>
<li><button @onclick="ClickBtn">Go to counter page</button></li>
</ul>
@code {
[Inject]
public required NavigationManager NavigationManager { get; set; }
public void ClickBtn()
{
NavigationManager.NavigateTo("/counter");
}
<li><a href="/counter">Go to counter page</a></li>
<li><NavLink href="/counter">Go to counter page</NavLink></li>
<li><button @onclick="ClickBtn">Go to counter page</button></li>
</ul>
@code {
[Inject]
public required NavigationManager NavigationManager { get; set; }
public void ClickBtn()
{
NavigationManager.NavigateTo("/counter");
}
Navigating
Just add a spinner or text to the router in a <Navigating> element.
<Router AppAssembly="@typeof(Program).Assembly" >
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<Navigating>
<p>Loading…</p>
</Navigating>
</Router>
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<Navigating>
<p>Loading…</p>
</Navigating>
</Router>
NavigationLock
You can subscribe to navigation events from the NavigationManager.
<NavigationLock can intercept external redirects (actually via window.beforeunload- no real control) and internal navigation. Note internal redirects are only within the page (not menu navigation - you need a NavigationLock in your NavMenu for that).
<NavigationLock ConfirmExternalNavigation="true"
OnBeforeInternalNavigation="OnBeforeInternalNavigation" />
<EditForm OnSubmit="Save" EditContext="_editContext">
<p><InputNumber @bind-Value="_model.Count"/></p>
<button type="submit" disabled="@_formInvalid">Submit</button>
</EditForm>
<p>Is _editContext modified? @_editContext.IsModified()</p>
@code {
private EditContext _editContext = default!;
private bool _formInvalid = true;
private CounterModel _model = default!;
protected override void OnInitialized()
{
_model = new CounterModel();
_editContext = new(_model);
_editContext.OnFieldChanged += (_, __) =>
{
_formInvalid = !_editContext.Validate();
StateHasChanged();
};
}
[Inject]
public IJSRuntime JsRuntime { get; set; }
private async Task OnBeforeInternalNavigation(LocationChangingContext locationChangingContext)
{
if (_editContext.IsModified())
{
//or a modal blazor component
bool isConfirmed =
await JsRuntime.InvokeAsync<bool>("confirm", "Leave without saving?");
if (!isConfirmed)
{
locationChangingContext.PreventNavigation();
}
}
}
OnBeforeInternalNavigation="OnBeforeInternalNavigation" />
<EditForm OnSubmit="Save" EditContext="_editContext">
<p><InputNumber @bind-Value="_model.Count"/></p>
<button type="submit" disabled="@_formInvalid">Submit</button>
</EditForm>
<p>Is _editContext modified? @_editContext.IsModified()</p>
@code {
private EditContext _editContext = default!;
private bool _formInvalid = true;
private CounterModel _model = default!;
protected override void OnInitialized()
{
_model = new CounterModel();
_editContext = new(_model);
_editContext.OnFieldChanged += (_, __) =>
{
_formInvalid = !_editContext.Validate();
StateHasChanged();
};
}
[Inject]
public IJSRuntime JsRuntime { get; set; }
private async Task OnBeforeInternalNavigation(LocationChangingContext locationChangingContext)
{
if (_editContext.IsModified())
{
//or a modal blazor component
bool isConfirmed =
await JsRuntime.InvokeAsync<bool>("confirm", "Leave without saving?");
if (!isConfirmed)
{
locationChangingContext.PreventNavigation();
}
}
}