如何在Blazor中从页面传递值到布局?

16

我有一个布局 (MainLayout.razor),它有一个名为 ShowFooter 的标志。在某些页面上,我想将该标志设置为 true,而在另一些页面上则设置为 false.

我没能找到任何清晰的指示,关于一个页面(即具有路由的组件)如何与其布局通信。在Blazor中该如何实现呢?

注意: 你可能会建议使用两个布局,一个有页脚,另一个没有,但这并不能真正解决我的问题,因为我想能够在同一页上的不同时刻显示和隐藏页脚。此外,这只是一种需要在布局和页面之间进行通信的情况之一,还有无数其他情况。

3个回答

30

最简单的方法是在MainLayout组件中定义一个名为ShowFooter的公共布尔属性,代码如下:

public bool ShowFooter {get; set;}

要将对MainLayout组件的引用级联到给定组件中,可以通过在一个CascadingValue组件中包装标记,并将其Value属性设置为this来实现,如下所示:

@inherits LayoutComponentBase


<CascadingValue Value="this">
     <div class="sidebar">
        <NavMenu />
    </div>
    <div class="main">
         <div class="content px-4">
            @Body
        </div>
    </div>
</CascadingValue>
@code
{
    public bool ShowFooter {get; set;}

     protected override void OnInitialized()
    {
      // Put here code that checks the value of ShowFooter and acts in 
      // accordance with your dear wishes

     }
}

在 Index.razor 中的使用

@code{
     // Gets a reference to the MainLayout component
    [CascadingParameter]
    public MainLayout Layout { get; set; } 

    protected override void OnInitialized()
    {
        Layout.ShowFooter= true;
    
    }
}

2
注意:AppState模式处理组件作为整体的状态。 - enet
1
显然,即使我调用StateHasChanged()方法,当我更改组件中的公共属性时,布局似乎不会重新呈现。这是真的吗? - aradalvand
1
明白了,谢谢。顺便问一下,就像我之前说的,在 UI 事件中更改其他组件上布局的公共属性似乎不会重新渲染布局。这是这种方法的限制吗?还是我做错了什么? - aradalvand
11
或者,你可以把它留给其他人也获益。 - Kevin
2
哇,我感觉我差点回答了我的问题... - mack
显示剩余8条评论

7
有几种方法可以实现:
  1. 最丑陋的方法:如果你有两个模板,你可以在页面/组件的顶部使用以下代码选择要使用的模板:

    @layout NoFooterLayoutName

  2. 在模板中使用级联值(我建议你的场景使用这种方法):

<CascadingValue Value="Footer">
    <Child />
</CascadingValue>

示例fiddle:https://blazorfiddle.com/s/05spcuyk

文档:https://learn.microsoft.com/en-us/aspnet/core/blazor/components/cascading-values-and-parameters?view=aspnetcore-5.0

  1. 创建一个状态服务并将其添加为作用域内的启动项。状态服务具有页/组件中可注入的页脚布尔变量,可以在变量中使用:

在startup.cs的ConfigureService方法中:

services.AddScoped<AppState>();

在项目中的某个位置创建 AppState.cs 类(理想情况下应该放在 Services 文件夹中):

public class AppState 
{
   public bool ShowFooter { get; set; }
   public event Action StateChanged;
   private void NotifyStateChanged() => StateChanged?.Invoke();
}

然后在您的页面/组件中注入它,以便您可以更改ShowFooter元素,并且在模板中,您可以创建触发StateHasChanged()的事件处理程序(不确定是否必要):

@inject AppState _AppState;
@implements IDisposable
.
.
.
@code{
    protected override void OnInitialized()
    {
        _appState.StateChanged += StateChanged;
    }

    public void StateChanged()
    {
        StateHasChanged();
    }

    public void Dispose()
    {
        _appState.StateChanged -= StateChanged;
    }
}

1
谢谢。我喜欢这个。但是AppState类可以是一个静态类,而不是注册为单例服务吗?将其作为单例的优点是什么? - aradalvand
1
@Arad AppState类不应该是静态的,依赖注入将为每个用户/请求实例化该类(作用域),状态类应该被设计为保存用户状态 - 这可能不是您想要的。如果我正确理解了您的问题,最好的选择可能是在布局组件(MainLayout)上具有CascadingValue来显示/隐藏页脚(bool),以便页面可以在初始化覆盖方法时将其设置为任何他们喜欢的值... - diegorodny
@Arad... 请记住,每个页面都必须设置这个选项,否则就会出现某些页面隐藏页脚的情况。当导航到另一个页面时,如果页脚没有重置为true,则它将保持隐藏状态。 - diegorodny

0
你可以使用一个通知服务,并将其注入组件中。
public class NotifyService 
{
     public event Func<bool, Task> Notify;

     public async Task Notify(bool value) 
     {
        if (Notify is object)
        {
            await Notify.Invoke(value);
        }
     }
}

然后将其注册为单例(如果在服务器端则为作用域),并将其注入到您的组件中。


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