在 Blazor 中的页面和 NavMenu 之间交换数据

Exchange Data between Page and NavMenu in Blazor

我有一个标准的 - 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?我希望为此使用静态 class 并不是一个好的选择。

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

在这种情况下,级联值或状态容器可能是最佳选择。 级联值需要顶级组件来包含该值,例如封装了 <NavMenu>@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>

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

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

在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>

更好的scoped-service实现:

 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();
    }

我们不是在传递 objects 的字典,我们有一个只做一件事的简单服务。更改页面的唯一方法是调用 SetCurrentPageName,它会引发一个事件让消费者知道名称已更改。这在 un-nested 组件之间是必需的,因为更新不会以其他方式传播。

我们还需要在启动时注册服务(因为当前页面是 session-specific,所以范围限定):

            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();
    }

这个状态 class 不知道它是如何使用的,也没有 objects 或引用被传递。

[编辑] 我决定用于设置页面名称的 inject/override 模式相当 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!" />

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