繁体   English   中英

从子组件通知 MainLayout/App 属性更改

[英]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 对象包装成 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组件中有一个 static 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()}");

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM