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

7

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

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

主布局看起来像这样:

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

现在我想在我的页面(index.razor、sub1.razor)和导航菜单之间交换数据,以便我可以在导航菜单中添加类似以下内容的东西:
<div><p>You are now on Page: @CurrentPageName</p></div>

我该如何在我的页面中直接设置(navMenu).CurrentPageName?我觉得使用静态类并不是一个好的选择。
3个回答

11
更好的作用域服务实现:
 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

and further down..


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

这个状态类并不知道它是如何被使用的,并且没有对象或引用被传递。

[编辑] 我决定注入/覆盖模式来设置页面名称相当不符合Blazor的风格,所以我还编写了一个组件来简化这个过程 - 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!" />

整个消费者现在已经成为一个组件 :)

1
第一个解决方案不应该实现IDisposal并在释放时释放OnChange注册吗? - Jan

3
在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>和页面组件中。

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

-2
使用作用域服务共享数据/对象。
在 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>


2
感谢您尝试回答,但这并不是非常好的代码。我本来要解释所有的问题,但在评论中只有500个字符,而且需要更多的内容来涵盖所有的问题。我将发布一个使用作用域服务相同概念的解决方案来帮助您。 - Quango

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接