Blazor中具有C#代码的动态_Hosts.cshtml文件

时间:2019-12-05 14:52:29

标签: blazor

只需使用DevExpress的免费免费Blazor代码即可在运行时实现自定义<HEAD>数据:

_Hosts.cshtml(请参阅元素中的代码段):

    <!DOCTYPE html>
    <html lang="en">
    <head>
        @(await Html.RenderComponentAsync<DocumentMetadataComponent>(RenderMode.ServerPrerendered))
    </head>
    <body>
        <app>
            @(await Html.RenderComponentAsync<App>(RenderMode.ServerPrerendered))
        </app>
    <script src="_framework/blazor.server.js"></script>
</body>

到目前为止效果很好(可以使标题,描述等变得动态)。

现在的问题是,我必须在顶层设置一些变量以进行过度解释。 我想知道使用的网址并动态提供一些数据。

但是据我所知,我现在在每个组件(DocumentMetadataComponent + App)中只能做两次。

我正在MainLayout.razor中获取数据:

<CascadingValue Value="StoreData" Name="StoreData">
    @Body
</CascadingValue>

@code{
    public StoreCompleteDTO StoreData { get; set; }

    protected override async Task OnInitializedAsync()
    {
        using var tl = new TimeLogger($"MainLayout.razor OnInitializedAsync()");

        StoreData = await AppState.GetStoreData(My.StoreId);
    }
}

此StoreData变量必须在动态元数据中可用,因为我必须为其他商店选择不同的CSS文件...

希望说明清楚。

仅一次将目标称为:     StoreData =等待AppState.GetStoreData(My.StoreId);

因为这是Web服务调用,并且花费时间...

谢谢!

2 个答案:

答案 0 :(得分:1)

我建议在Blazor应用程序中提供某些信息的最佳方法是使用服务系统和依赖注入。

这意味着您可以控制数据的范围(全局?会话?),以及使其在所需的任何页面/组件上可用。以State容器的形式编写也将有助于使用。这是一个快速示例,仅设置页面标题,但是您可以将其扩展为覆盖<meta>

    /// <summary>
    /// A state container for head tags
    /// </summary>
    public class HeadState
    {
        /// <summary>
        /// Page title
        /// </summary>
        public string Title => _title;

        // internal store
        private string _title = "";

        /// <summary>
        /// Set the page title
        /// </summary>
        /// <param name="title"></param>
        public void SetTitle(string title)
        {
            if(!string.Equals(_title,title))
            {
                _title = title;
                HeadChanged?.Invoke();
            }
        }

        /// <summary>
        /// Event raised when data changes
        /// </summary>
        public event Action HeadChanged;
    }

HeadState类是一个状态容器,允许我们设置页面标题。 title属性是只读的,因此必须通过SetTitle进行设置,该属性会触发HeadChanged事件,以便消费者知道已对其进行了修改。我们需要在Startup.cs中将其注册为范围服务:

   // declare the HeadState for DI
   services.AddScoped<State.HeadState>();

要实现它,我们修改了_host.cshtml,根据您的问题在<head>部分中添加了一个呈现它的组件:

<head>
    @(await Html.RenderComponentAsync<HeadComponent>(RenderMode.Server))
</head>

HeadComponent注入状态并处理渲染和更新:

@inject State.HeadState head

<title>@head.Title</title>

@code
{
    // although the title is set on loading, if it is changed by a component we
    // need to call StateHasChanged to trigger Blazor to rerender since the event
    // that triggered it is outside this component
    protected override void OnInitialized()
    {
        head.HeadChanged += () =>
        {
            StateHasChanged();
        };
    }
}

要在页面(或任何其他组件!)上对其进行测试,我们只需注入状态对象并使用它

@page "/"
@inject State.HeadState head

<h1>Head Demo</h1>

<button @onclick='(()=> head.SetTitle("Hello"))'>Set title to Hello</button>
<button @onclick='(()=> head.SetTitle("World"))'>Set title to World</button>

在这里,我们只是出于演示目的使用<title>标签,但是您当然可以添加<meta>或其他值。我会避免修改CSS样式表,因为可能应该通过JS来完成,但是我不是100%肯定那是正确的。

答案 1 :(得分:0)

我使用以下程序包,该程序包允许您逐页更改元数据。支持Server,ServerPrerendered和WASM!而且超级好用:

https://github.com/jsakamoto/Toolbelt.Blazor.HeadElement

将其与ServerPrerendered应用程序配合使用还意味着该应用程序可以被Google的机器人完全抓取。