NavigationLock能否作用于父组件?封装ConfirmNavigation组件失效求助
问题
我封装了ConfirmNavigation组件,内部集成NavigationLock,通过@bind-HasUnsavedChanges绑定父页面的未保存状态,原本期望在各页面引入该组件就能实现未保存更改时的导航确认,但组件并未生效。我猜测NavigationLock仅作用于组件自身而非父组件,请问有没有办法让该组件正常工作,避免在每个页面重复编写代码?
组件代码
ConfirmNavigation.razor
<NavigationLock ConfirmExternalNavigation="@HasUnsavedChanges" OnBeforeInternalNavigation="OnBeforeInternalNavigation" />
ConfirmNavigation.razor.cs
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Routing; using Microsoft.AspNetCore.Mvc; namespace LouisHowe.web.Shared { /// <summary> /// Asks the user to confirm a navigation to another page if the page on has unsaved changes. /// </summary> public partial class ConfirmNavigation { /// <summary> /// The MessageBoxPopup that is in MainLayout for notifying the user of issues. /// </summary> [CascadingParameter] private PopupMessageBox PopupMessageBox { get; set; } = default!; /// <summary> /// true if the page has unsaved changes. If so, will ask the user if they want to leave the page. /// This must be bound to the parent page's HasUnsavedChanges property. /// </summary> [BindProperty] [Parameter] public bool HasUnsavedChanges { get; set; } [Parameter] public EventCallback<bool> HasUnsavedChangesChanged { get; set; } /// <inheritdoc /> protected override Task OnInitializedAsync() { ArgumentNullException.ThrowIfNull(PopupMessageBox); return base.OnInitializedAsync(); } private async Task OnBeforeInternalNavigation(LocationChangingContext context) { if (!HasUnsavedChanges) return; // if the click Yes then we want to leave the page. // we need to mark HasUnsavedChanges as false so we don't get asked again. if (await PopupMessageBox.Show("Leave Page", "Do you wish to discard the changes you have made?<br/>" + "Select No to stay on this page.<br/>" + "Select Yes to go to the new page you selected", PopupMessageBox.ButtonNo, PopupMessageBox.ButtonYes) == PopupMessageBox.ButtonYes) { HasUnsavedChanges = false; // gives the browser time to update the bound values await Task.Yield(); } else context.PreventNavigation(); } } }
页面引入方式
<ConfirmNavigation @bind-HasUnsavedChanges="@HasUnsavedChanges"></ConfirmNavigation>
解决方案
问题出在参数绑定逻辑和NavigationLock的生效细节上,以下是具体修复步骤:
修正双向绑定逻辑
移除[BindProperty]标记(这是MVC特性,不适用于Blazor组件参数),同时在修改HasUnsavedChanges时必须触发HasUnsavedChangesChanged回调,确保父组件的状态同步更新:// 移除[BindProperty] [Parameter] public bool HasUnsavedChanges { get; set; } [Parameter] public EventCallback<bool> HasUnsavedChangesChanged { get; set; } // 修改确认后的状态同步代码 if (await PopupMessageBox.Show(...) == PopupMessageBox.ButtonYes) { HasUnsavedChanges = false; await HasUnsavedChangesChanged.InvokeAsync(false); // 触发回调同步父组件 await Task.Yield(); }排查NavigationLock生效问题
NavigationLock本身作用于全局路由导航,并非仅组件自身。如果组件未生效,检查以下几点:- 父页面的
HasUnsavedChanges是否在表单修改时正确设置为true - 确认
PopupMessageBox已正常渲染并可用,避免因弹窗加载失败导致逻辑中断 - 可以在
OnBeforeInternalNavigation方法中添加日志,确认导航时是否触发了该方法
- 父页面的
可选:改用级联参数统一管理状态
若多个页面需要该功能,可创建全局状态容器,通过级联参数传递,避免每个页面手动绑定:// 全局未保存状态容器 public class UnsavedChangesState { public bool HasUnsavedChanges { get; set; } public event Action? StateChanged; public void SetState(bool state) { HasUnsavedChanges = state; StateChanged?.Invoke(); } }在
Program.cs注册为Scoped服务:builder.Services.AddScoped<UnsavedChangesState>();在
MainLayout中作为级联参数传递:<CascadingValue Value="@UnsavedChangesState"> @Body </CascadingValue> @code { [Inject] private UnsavedChangesState UnsavedChangesState { get; set; } = default!; }最后修改
ConfirmNavigation组件接收级联参数,页面直接修改状态容器即可:[CascadingParameter] private UnsavedChangesState UnsavedChangesState { get; set; } = default!; private async Task OnBeforeInternalNavigation(LocationChangingContext context) { if (!UnsavedChangesState.HasUnsavedChanges) return; if (await PopupMessageBox.Show(...) == PopupMessageBox.ButtonYes) { UnsavedChangesState.SetState(false); await Task.Yield(); } else context.PreventNavigation(); }
内容的提问来源于stack exchange,提问作者David Thielen
相关产品推荐
相关产品推荐

