You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的生效细节上,以下是具体修复步骤:

  1. 修正双向绑定逻辑
    移除[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();
    }
    
  2. 排查NavigationLock生效问题
    NavigationLock本身作用于全局路由导航,并非仅组件自身。如果组件未生效,检查以下几点:

    • 父页面的HasUnsavedChanges是否在表单修改时正确设置为true
    • 确认PopupMessageBox已正常渲染并可用,避免因弹窗加载失败导致逻辑中断
    • 可以在OnBeforeInternalNavigation方法中添加日志,确认导航时是否触发了该方法
  3. 可选:改用级联参数统一管理状态
    若多个页面需要该功能,可创建全局状态容器,通过级联参数传递,避免每个页面手动绑定:

    // 全局未保存状态容器
    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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 08:17:47