如何将值从页面传递到 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 属性设置为 this
的 CascadingValue
组件中,如下所示:
@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 中的布局?的主要内容,如果未能解决你的问题,请参考以下文章