在 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!" />
整个消费者现在是一个组件:)
我有一个标准的 - 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!" />
整个消费者现在是一个组件:)