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

Posted

技术标签:

【中文标题】如何将值从页面传递到 Blazor 中的布局?【英文标题】:How to pass a value from a page to the layout in Blazor? 【发布时间】:2021-03-19 18:25:59 【问题描述】:

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

我还没有找到任何关于页面(即带有路由的组件)如何与其布局进行通信的明确说明。如何/应该在 Blazor 中做到这一点?

注意:您可能建议使用两种布局,一种有页脚,一种没有页脚,但这并不能真正解决我的问题,我希望能够以不同的方式显示和隐藏页脚同一页上的次数。另外,这只是需要在布局和页面之间进行通信的一种情况。还有无数其他的。

【问题讨论】:

【参考方案1】:

您可以使用通知服务并将其注入到组件中。

public class NotifyService 

     public event Func<bool, Task> Notify;

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

然后在 DI 容器中将其注册为单例(如果是服务器端,则为范围)并将其注入到您的组件中。

【讨论】:

【参考方案2】:

有几种方法可以做到:

    最丑的:如果你有两个模板,你可以简单地在页面/组件的顶部选择你想要使用的模板:

    @layout NoFooterLayoutName

    在模板中使用级联值(我会为您的场景推荐的内容):

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

小提琴示例: https://blazorfiddle.com/s/05spcuyk

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

    创建状态服务并将其添加到启动范围内。带有footer bool 变量的状态服务,然后可以注入到使用的页面/组件和变量中:

在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;
    

【讨论】:

谢谢。我喜欢这个。但是AppState 类可以是静态类,而不是注册为单例服务吗?将其作为单例有什么好处? @Arad AppState 类不应该是静态的,依赖注入将根据用户/请求(范围)实例化该类,状态类应该设计为保存用户状态 - 这可能不是你的想要它做。如果正确理解您的问题,最好的选择可能是让 CascadingValue 在布局组件(MainLayout)上显示/隐藏页脚(bool),以便页面可以将其设置为初始化覆盖方法上可能喜欢的任何内容.... @Arad ...但请记住,每个页面都必须设置此项,否则您会遇到某些页面将隐藏页脚的情况,然后在导航到另一个页面时,如果页脚未重置为真,它将保持隐藏状态。【参考方案3】:

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

public bool ShowFooter get; set;

并将对 MainLaout 组件的引用级联到给定组件,方法是将标记包装在 Value 属性设置为 thisCascadingValue 组件中,如下所示:

@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;
    
    

【讨论】:

谢谢,这很聪明。但是在这与史蒂夫桑德森提到的here 的AppState 模式之间,您认为哪种方法更好?你会说这是 hackier 吗? 有几种方法可以做到这一点。在***中寻找我的答案,这一切都取决于你想做什么。不,这不是一种更老套的方法。这绝对是做到这一点的方法。史蒂夫桑德森提到 s 的方式是 hacky 方式。它是在 Blazor 开发的早期编写的,当时 Blazor 没有当前的功能和支持的内置组件。这样做。 注意:AppState 模式将组件的状态作为一个整体来处理.. 好的。那我就用这个!谢谢。最后一个问题,我会将您的答案标记为已接受:既然我们正在这样做,我们不妨在MainLayout 上拥有公共ElementReference 属性:假设我在MainLayout 中有一个Toast 组件,它公开像Show()Hide()这样的方法,考虑在MainLayout中声明一个ElementReference类型的公共属性引用Toast组件,然后在其他组件中我可以访问该toast组件并执行操作喜欢Layout.Toast.Show()。您认为这是一种合理的方法还是很糟糕?! 显然,即使我调用StateHasChanged() 方法,当我更改组件中的公共属性时,布局也不会重新呈现。这是真的吗?

以上是关于如何将值从页面传递到 Blazor 中的布局?的主要内容,如果未能解决你的问题,请参考以下文章

将值从部分值传递到 mvc 中的布局视图

如何将值从一个 html 页面传递到 PHONEGAP 中的另一个 html 页面?

如何将值从 ajax 传递到 portlet 页面

将值从模型传递到 CI 中的视图

如何将值从 XIB 传递到 UIView 初始化程序?

如何将值从片段/活动传递到 xml?