【问题标题】:Exchange Data between Page and NavMenu in Blazor在 Blazor 中的 Page 和 NavMenu 之间交换数据
【发布时间】:2020-07-09 18:28:30
【问题描述】:

我有一个标准 - blazor - 项目,其中包含以下组件:

-> MainLayout.razor
-> NavMenu.razor
-> Pages/Index.razor
-> Pages/Sub1.razor

MainLayout 如下所示:

<div class="sidebar">
    <NavMenu />
</div>
<div>@Body</div>

现在我想在我的页面(index.razor、sub1.razor)和导航菜单之间交换数据,这样我就可以在导航菜单中添加类似这样的内容:

<div><p>You are now on Page: @CurrentPageName</p></div>

如何在我的页面中直接设置 (navMenu).CurrentPageName?我希望为此使用静态类并不是一个好的选择。

【问题讨论】:

    标签: c# blazor razor-pages blazor-server-side


    【解决方案1】:

    更好的范围服务实现:

     public class CurrentPage
        {
            public string CurrentPageName { get; private set; }
    
            public void SetCurrentPageName(string name)
            {
                if (!string.Equals(CurrentPageName, name)) 
                {
                    CurrentPageName = name;
                    NotifyStateChanged();
                }
            }
    
            public event Action OnChange; // event raised when changed
    
            private void NotifyStateChanged() => OnChange?.Invoke();
        }
    

    我们不是在传递对象的字典,我们有一个做一件事的简单服务。更改页面的唯一方法是调用SetCurrentPageName,它会引发一个事件让消费者知道名称已更改。这在非嵌套组件之间是必需的,因为否则更新不会传播。

    我们还需要在启动时注册服务(因为当前页面是特定于会话的):

                services.AddScoped<CurrentPage>();
    

    我们将注入Index.razor,并使用它:

    @page "/"
    @inject CurrentPage currentPage
    
    <h1>Hello, world!</h1>
    
    Welcome to your new app.
    <button @onclick="ChangeName">Set Page Name</button>
    
    <SurveyPrompt Title="How is Blazor working for you?" />
    @code
    {
    
        protected override void OnInitialized()
        {
            currentPage.SetCurrentPageName("The Home Page");
            base.OnInitialized();
        }
    
        void ChangeName() => currentPage.SetCurrentPageName("Name changed");
    }
    

    最后在NavMenu.razor的顶部:

    @inject CurrentPage currentPage
    

    再往下走。。

    
        <p>The current page is @currentPage.CurrentPageName</p>
    
    @code {
        protected override void OnInitialized()
        {
            // if the OnChange event is raised, refresh this view
            currentPage.OnChange += () => StateHasChanged();
    
            base.OnInitialized();
        }
    

    这个状态类对它的使用方式一无所知,也没有传递任何对象或引用。

    [编辑] 我决定设置页面名称的注入/覆盖模式相当 unBlazor,所以我还编写了一个组件来简化这一点 - PageName.razor:

    @inject CurrentPage currentPage;
    
    @code {
        [Parameter]
        public string Name { get; set; }
    
        protected override void OnParametersSet()
        {
            currentPage.SetCurrentPageName(Name);
        }
    }
    

    现在任何想要设置标题的页面都可以这样做:

    @page "/fetchdata"
    @inject HttpClient Http
    <PageName Name="Weather forecast page!" />
    

    整个消费者现在是一个组件:)

    【讨论】:

    • 第一个解决方案不应该实现 IDisposal 并在处置时释放 OnChange 注册吗?
    【解决方案2】:

    Blazor 中的组件之间通信的主要方式有三种。 Chris Sainty 有一篇很好的文章概述了这些:https://chrissainty.com/3-ways-to-communicate-between-components-in-blazor/

    在这种情况下,级联值或状态容器可能是最佳选择。 级联值需要一个顶级组件来包含该值,例如封装&lt;NavMenu&gt;@Body 的东西:

    @inherits LayoutComponentBase
    <MenuState>
    <div class="sidebar">
        <NavMenu />
    </div>
    
    <div class="main">
        <div class="top-row px-4">
            <a href="http://blazor.net" target="_blank" class="ml-md-auto">About</a>
        </div>
    
        <div class="content px-4">
            @Body
        </div>
    </div>
    </MenuState>
    

    另一种方法是使用提供状态服务的可注入服务,您可以将其注入到&lt;NavMenu&gt; 和页面组件中。

    【讨论】:

    • 如何使用从“顶级组件”到“低级”(子)组件的级联值?数据流不是下游,而是上游。他想在 FetchData 页面中发出一条消息,并在 LayoutComponent ( navmenu ) 中查看它
    • 我能够使用“状态容器” - 网址中描述的方式
    • @enet 组件可以通过级联值将参数向下传递给子组件。没有什么说值必须是值对象,它可以是提供必要功能的类,例如状态容器。这是使用 DI 的替代方法。两种方法都有效
    • @Quango,你说的是真的,但与我在评论中提出的问题无关:他想在 FetchData 页面中发布一条消息,并在 LayoutComponent ( navmenu )...你是怎么做到的。
    • @Ole Albers,请发布您的工作解决方案,以便我们都能从您的好问题中受益。
    【解决方案3】:

    使用范围服务共享数据/对象

    在 Startup.cs 中

    您可以添加自己的“Scoped”服务:

    public void ConfigureServices(IServiceCollection services)
    {
       ......
       services.AddScoped<MyScopeService>();
    }
    
    public class MyScopeService : Dictionary<string,object>
    {
    } 
    

    在 NavMenu.razor 中

    @inject MyScopeService mss
    
    @code{
        string _title;
        public void UpdateTitle(string title)
        {
            _title = title;
            StateHasChanged();
        }
    }
    
    @{
        mss["NavMenu"] = this;
    }
    <div>@_title</div>
    

    在目标页面中:

    
    @inject MyScopeService mss
    @code{
        void SetTitle()
        {
            NavMenu menu = (NavMenu)mss["NavMenu"];
            menu.UpdateTitle("Hello this is page 1");
        }
    }
    
    <button @onclick="SetTitle">SetTitle</button>
    
    

    【讨论】:

    • 感谢您尝试回答,但这并不是很好的代码。我本来打算解释所有不好的地方,但评论中只有 500 个字符,要涵盖这里的所有问题需要更多的时间。我将使用与范围服务相同的概念发布解决方案来帮助您。
    猜你喜欢
    • 2017-09-01
    • 1970-01-01
    • 2021-09-21
    • 1970-01-01
    • 2013-04-04
    • 1970-01-01
    • 2023-04-07
    • 2021-09-11
    • 1970-01-01
    相关资源
    最近更新 更多