提问人:Zumpel 提问时间:4/26/2022 更新时间:4/27/2022 访问量:658
通知 MainLayout/App 子组件的属性更改
Notify MainLayout/App of Property Change from Child Component
问:
我有一个应用程序,它由一个简单的布局组成,带有侧边栏和标题。标题有一个文本,显示一些自定义信息(例如,用户名、日期或只是当前视图的名称)。
<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 对象包装到 Blazor 组件中。
我没有将此代码放入我的代码中,因为我希望在找不到页面或用户没有特定页面的权限时显示侧边栏和标题。MainLayout.razor
我遇到的问题是,我无法找到一个干净的解决方案来更新我的标题。目前我是这样做的,但对我来说,它看起来是一个非常“黑客”的解决方案,如果没有调用 .StateHasChanged
我创建了一个名为 的类,我的所有页面都从该类继承,并将标题作为级联参数。ViewBase.cs
<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
这允许我通过编写 .这实现了基本功能,但我还想直接在 Blazor 视图中控制标题。因此,我创建了一个简单的组件。this.Title = "My custom Title!"
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>
正如我所说,这在某种程度上有效,但有点麻烦,特别是如果您需要每秒更新标题几次(这是一项要求)。我监督过某种机制吗?我认为这很好,如果可以通过一个事件来处理,其中新值在层次结构中向上传递( -> -> ),但我想不出如何做到这一点的方法。我能想到的另一个解决方案是,我的组件中有一个静态的 oldscool C# 事件,它由我的组件调用。StateHasChanged
View.razor
MainLayout.razor
App.cshtml
Header
HeaderControl
答:
您需要将服务与通知事件一起使用。这里有一个非常简单的实现来演示这个原理。
标头服务
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>();
标头组件
@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()}");
}
评论