通知 MainLayout/App 来自 Child 组件的 属性 更改
Notify MainLayout/App of Property Change from Child Component
我有一个应用程序,它包含一个带有边栏的简单布局和 header。 header 有一个显示一些自定义信息的文本(例如,用户名、日期或只是当前视图的名称)。
<Layout Sider="true">
<LayoutSider>
<LayoutSiderContent>
<NavMenu />
</LayoutSiderContent>
</LayoutSider>
<Layout>
<LayoutHeader Fixed="true">
<Header Title="@Title"></Header> <!-- CUSTOM HEADER COMPONENT -->
</LayoutHeader>
<LayoutContent>
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true"> <!-- VIEW ROUTER-->
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
<NotAuthorized>
You are not Authorized to Access this App. Please make sure, that you logged in with your company account.
</NotAuthorized>
</AuthorizeRouteView>
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>
</LayoutContent>
</Layout>
</Layout>
@code
{
private string Title { get; set; }
}
注:Header是我写的一个简单的组件,只是输出一段文字,有点CSS。我还使用了 Blazorise,它将基本 HTML objects 包装到 Blazor 组件中。
我没有将此代码放入我的 MainLayout.razor
中,因为我希望在找不到页面或用户没有特定页面的权限时显示边栏和 header。
我遇到的问题是,我无法找到一个干净的解决方案来更新我的标题。目前我是这样做的,如下所示,但对我来说,它看起来像是一个非常“hacky”的解决方案,它不会在不调用 StateHasChanged
.
的情况下更新标题
我制作了一个名为 ViewBase.cs
的 class,我所有的页面都继承自它并将标题作为级联参数。
<Layout Sider="true">
<CascadingValue Value="@Title">
<Layout>
<LayoutHeader Fixed="true">
...
</LayoutHeader>
<LayoutContent>
...
</LayoutContent>
</Layout>
</CascadingValue>
</Layout>
^ 调整后 App.cshtml
具有级联值
@inherits LayoutComponentBase
<CascadingValue Value="@Title">
@Body
</CascadingValue>
@code
{
private string title;
[CascadingParameter]
public string Title
{
get => this.title;
set
{
this.title = value;
this.StateHasChanged();
}
}
}
^MainLayout.razor
public class ViewBase : ComponentBase
{
private string title;
[CascadingParameter]
public string Title
{
get => this.title;
set
{
this.title = value;
this.StateHasChanged();
}
}
}
^ViewBase.cs
这允许我通过编写 this.Title = "My custom Title!"
在我的视图中以编程方式设置标题。这实现了基本功能,但我还想直接在 Blazor 视图中控制标题。因此,我创建了一个简单的组件 HeaderControl
.
@inherits Project.Shared.BaseComponents.ViewBase
@code {
[Parameter]
public string PTitle
{
get => this.Title;
set => this.Title = value;
}
}
然后我可以像这样在我的视图中使用这个组件:
@page "/"
@inherits Project.Shared.BaseComponents.ViewBase
<HeaderControl PTitle="Hello World!"/>
<Div Class="w-100 h-100 d-flex flex-column align-items-center justify-content-between">
<!-- Page Content -->
</Div>
正如我所说,这有点管用,但是 StateHasChanged
有点麻烦,特别是如果您需要每秒更新几次标题(这是一项要求)。我监督过某种机制吗?我认为这会很好,如果这可以通过一个事件来处理,新值在层次结构中向上传递(View.razor
-> MainLayout.razor
-> App.cshtml
),但我不能想办法做到这一点。我能想到的另一个解决方案是,我的 Header
组件中有一个静态的 oldscool C# 事件,它被我的 HeaderControl
组件调用。
您需要使用带有通知事件的服务。这里有一个非常简单的实现来演示原理。
一个Header服务
public class HeaderService
{
public string Header { get; set; } = "Starting Header";
public event EventHandler? HeaderChanged;
public void SetHeader(string header)
{
Header = header;
HeaderChanged?.Invoke(this, EventArgs.Empty);
}
public void NotifyHeaderChanged()
=> HeaderChanged?.Invoke(this, EventArgs.Empty);
}
在Program
注册:
builder.Services.AddScoped<HeaderService>();
一个Header组件
@inject HeaderService headerService
@implements IDisposable
<h3>@this.headerService.Header</h3>
@code {
protected override void OnInitialized()
=> this.headerService.HeaderChanged += this.OnChange;
private void OnChange(object? sender, EventArgs e)
=> this.InvokeAsync(StateHasChanged);
public void Dispose()
=> this.headerService.HeaderChanged -= this.OnChange;
}
应用程序:
<MyHeader />
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
演示页面:
@page "/"
@inject HeaderService headerService
<PageTitle>Index</PageTitle>
<div class="p-3">
<button class="btn btn-primary" @onclick=UpdateHeader>Say Hello</button>
</div>
Welcome to your new app.
@code {
private void UpdateHeader()
=> this.headerService.SetHeader($"Clicked at {DateTime.Now.ToLongTimeString()}");
}
我有一个应用程序,它包含一个带有边栏的简单布局和 header。 header 有一个显示一些自定义信息的文本(例如,用户名、日期或只是当前视图的名称)。
<Layout Sider="true">
<LayoutSider>
<LayoutSiderContent>
<NavMenu />
</LayoutSiderContent>
</LayoutSider>
<Layout>
<LayoutHeader Fixed="true">
<Header Title="@Title"></Header> <!-- CUSTOM HEADER COMPONENT -->
</LayoutHeader>
<LayoutContent>
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true"> <!-- VIEW ROUTER-->
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
<NotAuthorized>
You are not Authorized to Access this App. Please make sure, that you logged in with your company account.
</NotAuthorized>
</AuthorizeRouteView>
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>
</LayoutContent>
</Layout>
</Layout>
@code
{
private string Title { get; set; }
}
注:Header是我写的一个简单的组件,只是输出一段文字,有点CSS。我还使用了 Blazorise,它将基本 HTML objects 包装到 Blazor 组件中。
我没有将此代码放入我的 MainLayout.razor
中,因为我希望在找不到页面或用户没有特定页面的权限时显示边栏和 header。
我遇到的问题是,我无法找到一个干净的解决方案来更新我的标题。目前我是这样做的,如下所示,但对我来说,它看起来像是一个非常“hacky”的解决方案,它不会在不调用 StateHasChanged
.
我制作了一个名为 ViewBase.cs
的 class,我所有的页面都继承自它并将标题作为级联参数。
<Layout Sider="true">
<CascadingValue Value="@Title">
<Layout>
<LayoutHeader Fixed="true">
...
</LayoutHeader>
<LayoutContent>
...
</LayoutContent>
</Layout>
</CascadingValue>
</Layout>
^ 调整后 App.cshtml
具有级联值
@inherits LayoutComponentBase
<CascadingValue Value="@Title">
@Body
</CascadingValue>
@code
{
private string title;
[CascadingParameter]
public string Title
{
get => this.title;
set
{
this.title = value;
this.StateHasChanged();
}
}
}
^MainLayout.razor
public class ViewBase : ComponentBase
{
private string title;
[CascadingParameter]
public string Title
{
get => this.title;
set
{
this.title = value;
this.StateHasChanged();
}
}
}
^ViewBase.cs
这允许我通过编写 this.Title = "My custom Title!"
在我的视图中以编程方式设置标题。这实现了基本功能,但我还想直接在 Blazor 视图中控制标题。因此,我创建了一个简单的组件 HeaderControl
.
@inherits Project.Shared.BaseComponents.ViewBase
@code {
[Parameter]
public string PTitle
{
get => this.Title;
set => this.Title = value;
}
}
然后我可以像这样在我的视图中使用这个组件:
@page "/"
@inherits Project.Shared.BaseComponents.ViewBase
<HeaderControl PTitle="Hello World!"/>
<Div Class="w-100 h-100 d-flex flex-column align-items-center justify-content-between">
<!-- Page Content -->
</Div>
正如我所说,这有点管用,但是 StateHasChanged
有点麻烦,特别是如果您需要每秒更新几次标题(这是一项要求)。我监督过某种机制吗?我认为这会很好,如果这可以通过一个事件来处理,新值在层次结构中向上传递(View.razor
-> MainLayout.razor
-> App.cshtml
),但我不能想办法做到这一点。我能想到的另一个解决方案是,我的 Header
组件中有一个静态的 oldscool C# 事件,它被我的 HeaderControl
组件调用。
您需要使用带有通知事件的服务。这里有一个非常简单的实现来演示原理。
一个Header服务
public class HeaderService
{
public string Header { get; set; } = "Starting Header";
public event EventHandler? HeaderChanged;
public void SetHeader(string header)
{
Header = header;
HeaderChanged?.Invoke(this, EventArgs.Empty);
}
public void NotifyHeaderChanged()
=> HeaderChanged?.Invoke(this, EventArgs.Empty);
}
在Program
注册:
builder.Services.AddScoped<HeaderService>();
一个Header组件
@inject HeaderService headerService
@implements IDisposable
<h3>@this.headerService.Header</h3>
@code {
protected override void OnInitialized()
=> this.headerService.HeaderChanged += this.OnChange;
private void OnChange(object? sender, EventArgs e)
=> this.InvokeAsync(StateHasChanged);
public void Dispose()
=> this.headerService.HeaderChanged -= this.OnChange;
}
应用程序:
<MyHeader />
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
演示页面:
@page "/"
@inject HeaderService headerService
<PageTitle>Index</PageTitle>
<div class="p-3">
<button class="btn btn-primary" @onclick=UpdateHeader>Say Hello</button>
</div>
Welcome to your new app.
@code {
private void UpdateHeader()
=> this.headerService.SetHeader($"Clicked at {DateTime.Now.ToLongTimeString()}");
}