Blazor:从布局访问参数
Blazor: Access parameter from layout
如何从布局访问页面的路由参数?
我有一个页面接受如下路由参数:
@page /my-page/{Slug}
在共享布局中呈现标记时,我需要访问 Slug
的值。
我试过在布局文件中实现 OnParametersSet
,如下所示,但未设置该值。它仅在页面级别分配。
@inherits LayoutComponentBase
<div class="sidebar">
<NavMenu />
</div>
<div class="main">
<div class="top-row px-4">
@this.Slug <<<<------ display the parameter
</div>
<div class="content px-4">
@Body
</div>
</div>
@code
{
[Parameter]
public string Slug { get; set; }
protected override void OnParametersSet()
{
// Slug is always null :-/
}
}
经过一番摸索,我想出了以下解决方案。它可能都可以在 .razor
文件中完成,但我在“代码隐藏”文件中实现了一些混乱以隐藏似乎是杂乱无章的东西。
在 Layout 实例中,如果您覆盖 OnParametersSet
,并钻取 Body.Target
,您将找到包含路由参数的 RouteData
。然后,您可以将这些值传播到布局中的子组件。
包含我们希望布局可用的“Slug”参数的页面
@page "/mypage/{Slug}"
布局 .razor 文件
@inherits Components.MyLayoutBase
<div class="sidebar">
<!-- Pass the Slug property to the NavMenu's Parameter -->
<MyNavMenu Slug="@Slug" />
</div>
<div class="main">
<div class="top-row px-4"></div>
<div class="content px-4">
@Body
</div>
</div>
后面的布局代码
public class MyLayoutBase : LayoutComponentBase
{
public string Slug { get; set; }
protected override void OnParametersSet()
{
// pull out the "Slug" parameter from the route data
object slug = null;
if ((this.Body.Target as RouteView)?.RouteData.RouteValues?.TryGetValue("Slug", out slug) == true)
{
Slug = slug?.ToString();
}
}
}
导航菜单
<div class="top-row pl-4 navbar navbar-dark">
</div>
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
<ul class="nav flex-column">
<li class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="oi oi-home" aria-hidden="true"></span> Home
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="@($"/some-page/{Slug}/foo")">
<span class="oi oi-home" aria-hidden="true"></span> Foo
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="@($"/some-page/{Slug}/bar")">
<span class="oi oi-home" aria-hidden="true"></span> Bar
</NavLink>
</li>
</ul>
</div>
@code {
[Parameter]
public string Slug { get; set; }
bool collapseNavMenu = true;
string NavMenuCssClass => collapseNavMenu ? "collapse" : null;
string setupUrl = string.Empty;
void ToggleNavMenu()
{
collapseNavMenu = !collapseNavMenu;
}
}
您可以通过 CascadingParameters 使 RouteData 可用于所有组件。在您的 App.razor 中,执行此操作:
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<CascadingValue Value="@routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MyLayout)" />
</CascadingValue>
</Found>
</Router>
然后,在需要 RouteData 的所有组件中,只需添加:
@code
{
[CascadingParameter]
RouteData RouteData { get; set; }
}
该值将自动填充。
上面的答案没有解决嵌套布局。在那些情况下,我得到了以下工作。级联参数方法也有一些潜在的问题:
public static bool TryGetRouteData(this Delegate renderFragment, out RouteData routeData)
{
routeData = null;
if (renderFragment?.Target is RouteView routeView)
{
routeData = routeView.RouteData;
}
else if (renderFragment?.Target is object target)
{
// the target may be a CompilerGenerated class
var bodyField = target.GetType().GetField("bodyParam");
if (bodyField is not null)
{
var value = bodyField.GetValue(target);
if (value is Delegate childDel)
{
TryGetRouteData(childDel, out routeData);
}
}
}
return routeData is not null;
}
然后在 layout/component class:
protected override void OnInitialized()
{
if (Body?.TryGetRouteData(out var routeData) == true)
{
// stuff here
}
}
如何从布局访问页面的路由参数?
我有一个页面接受如下路由参数:
@page /my-page/{Slug}
在共享布局中呈现标记时,我需要访问 Slug
的值。
我试过在布局文件中实现 OnParametersSet
,如下所示,但未设置该值。它仅在页面级别分配。
@inherits LayoutComponentBase
<div class="sidebar">
<NavMenu />
</div>
<div class="main">
<div class="top-row px-4">
@this.Slug <<<<------ display the parameter
</div>
<div class="content px-4">
@Body
</div>
</div>
@code
{
[Parameter]
public string Slug { get; set; }
protected override void OnParametersSet()
{
// Slug is always null :-/
}
}
经过一番摸索,我想出了以下解决方案。它可能都可以在 .razor
文件中完成,但我在“代码隐藏”文件中实现了一些混乱以隐藏似乎是杂乱无章的东西。
在 Layout 实例中,如果您覆盖 OnParametersSet
,并钻取 Body.Target
,您将找到包含路由参数的 RouteData
。然后,您可以将这些值传播到布局中的子组件。
包含我们希望布局可用的“Slug”参数的页面
@page "/mypage/{Slug}"
布局 .razor 文件
@inherits Components.MyLayoutBase
<div class="sidebar">
<!-- Pass the Slug property to the NavMenu's Parameter -->
<MyNavMenu Slug="@Slug" />
</div>
<div class="main">
<div class="top-row px-4"></div>
<div class="content px-4">
@Body
</div>
</div>
后面的布局代码
public class MyLayoutBase : LayoutComponentBase
{
public string Slug { get; set; }
protected override void OnParametersSet()
{
// pull out the "Slug" parameter from the route data
object slug = null;
if ((this.Body.Target as RouteView)?.RouteData.RouteValues?.TryGetValue("Slug", out slug) == true)
{
Slug = slug?.ToString();
}
}
}
导航菜单
<div class="top-row pl-4 navbar navbar-dark">
</div>
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
<ul class="nav flex-column">
<li class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="oi oi-home" aria-hidden="true"></span> Home
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="@($"/some-page/{Slug}/foo")">
<span class="oi oi-home" aria-hidden="true"></span> Foo
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="@($"/some-page/{Slug}/bar")">
<span class="oi oi-home" aria-hidden="true"></span> Bar
</NavLink>
</li>
</ul>
</div>
@code {
[Parameter]
public string Slug { get; set; }
bool collapseNavMenu = true;
string NavMenuCssClass => collapseNavMenu ? "collapse" : null;
string setupUrl = string.Empty;
void ToggleNavMenu()
{
collapseNavMenu = !collapseNavMenu;
}
}
您可以通过 CascadingParameters 使 RouteData 可用于所有组件。在您的 App.razor 中,执行此操作:
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<CascadingValue Value="@routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MyLayout)" />
</CascadingValue>
</Found>
</Router>
然后,在需要 RouteData 的所有组件中,只需添加:
@code
{
[CascadingParameter]
RouteData RouteData { get; set; }
}
该值将自动填充。
上面的答案没有解决嵌套布局。在那些情况下,我得到了以下工作。级联参数方法也有一些潜在的问题:
public static bool TryGetRouteData(this Delegate renderFragment, out RouteData routeData)
{
routeData = null;
if (renderFragment?.Target is RouteView routeView)
{
routeData = routeView.RouteData;
}
else if (renderFragment?.Target is object target)
{
// the target may be a CompilerGenerated class
var bodyField = target.GetType().GetField("bodyParam");
if (bodyField is not null)
{
var value = bodyField.GetValue(target);
if (value is Delegate childDel)
{
TryGetRouteData(childDel, out routeData);
}
}
}
return routeData is not null;
}
然后在 layout/component class:
protected override void OnInitialized()
{
if (Body?.TryGetRouteData(out var routeData) == true)
{
// stuff here
}
}